lesthegrngo Posted October 7, 2023 Posted October 7, 2023 (edited) ****edited as the basic issue is resolved, just leaving the discussion for reference**** All, this thread https://forum.dcs.world/topic/318814-increasing-the-sensitivity-of-ir-digital-position-sensor/#comment-5290335 shows what I have done so far, but I am being frustrated by the fact that I am not happy with the whole altimeter. The zeroing works great, as soon as you power up it will zero immediately and correctly. However once the gauge is used via USB in game it is not so good, with despite playing around with the stepper config max steps it never really matches the pointer position correctly, exacerbated by the movement not being constant - to explain, even in a steady descent with the in game pointer rotating at a steady speed, the physical pointer will more to fast for part of the rotation, and then slow down too much for another period, then speed up etc. It seems random, so the sketch I am using isn't really keeping control of the pointer. Things are worse still on trying to go to RS485; I managed to get it 'working' but it stutters constantly and is just not good. This is the code I'm using //#define DCSBIOS_IRQ_SERIAL #define DCSBIOS_RS485_SLAVE 48 #define TXENABLE_PIN 2 #include <AccelStepper.h> #include "DcsBios.h" #include <Wire.h> struct StepperConfig { unsigned int maxSteps; unsigned int acceleration; unsigned int maxSpeed; }; class Vid29Stepper : public DcsBios::Int16Buffer { private: AccelStepper& stepper; StepperConfig& stepperConfig; inline bool zeroDetected() { return digitalRead(irDetectorPin) == 1; } unsigned int (*map_function)(unsigned int); unsigned char initState; long currentStepperPosition; long lastAccelStepperPosition; unsigned char irDetectorPin; long zeroOffset; bool movingForward; bool lastZeroDetectState; long normalizeStepperPosition(long pos) { if (pos < 0) return pos + stepperConfig.maxSteps; if (pos >= stepperConfig.maxSteps) return pos - stepperConfig.maxSteps; return pos; } void updateCurrentStepperPosition() { // adjust currentStepperPosition to include the distance our stepper motor // was moved since we last updated it long movementSinceLastUpdate = stepper.currentPosition() - lastAccelStepperPosition; currentStepperPosition = normalizeStepperPosition(currentStepperPosition + movementSinceLastUpdate); lastAccelStepperPosition = stepper.currentPosition(); } public: Vid29Stepper(unsigned int address, AccelStepper& stepper, StepperConfig& stepperConfig, unsigned char irDetectorPin, long zeroOffset, unsigned int (*map_function)(unsigned int)): Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), irDetectorPin(irDetectorPin), zeroOffset(zeroOffset), map_function(map_function), initState(0), currentStepperPosition(0), lastAccelStepperPosition(0) { } virtual void loop() { if (initState == 0) { // not initialized yet pinMode(irDetectorPin, INPUT); stepper.setMaxSpeed(stepperConfig.maxSpeed); stepper.setSpeed(1000); initState = 1; } if (initState == 1) { // move off zero if already there so we always get movement on reset // (to verify that the stepper is working) if (zeroDetected()) { stepper.runSpeed(); } else { initState = 2; } } if (initState == 2) { // zeroing if (!zeroDetected()) { stepper.runSpeed(); } else { stepper.setAcceleration(stepperConfig.acceleration); stepper.runToNewPosition(stepper.currentPosition() + zeroOffset); // tell the AccelStepper library that we are at position zero stepper.setCurrentPosition(0); lastAccelStepperPosition = 0; // set stepper acceleration in steps per second per second // (default is zero) stepper.setAcceleration(stepperConfig.acceleration); lastZeroDetectState = true; initState = 3; } } if (initState == 3) { // running normally // recalibrate when passing through zero position bool currentZeroDetectState = zeroDetected(); if (!lastZeroDetectState && currentZeroDetectState && movingForward) { // we have moved from left to right into the 'zero detect window' // and are now at position 0 lastAccelStepperPosition = stepper.currentPosition(); currentStepperPosition = normalizeStepperPosition(zeroOffset); } else if (lastZeroDetectState && !currentZeroDetectState && !movingForward) { // we have moved from right to left out of the 'zero detect window' // and are now at position (maxSteps-1) lastAccelStepperPosition = stepper.currentPosition(); currentStepperPosition = normalizeStepperPosition(stepperConfig.maxSteps + zeroOffset); } lastZeroDetectState = currentZeroDetectState; if (hasUpdatedData()) { // convert data from DCS to a target position expressed as a number of steps long targetPosition = (long)map_function(getData()); updateCurrentStepperPosition(); long delta = targetPosition - currentStepperPosition; // if we would move more than 180 degree counterclockwise, move clockwise instead if (delta < -((long)(stepperConfig.maxSteps / 2))) delta += stepperConfig.maxSteps; // if we would move more than 180 degree clockwise, move counterclockwise instead if (delta > (stepperConfig.maxSteps / 2)) delta -= (long)stepperConfig.maxSteps; movingForward = (delta >= 0); // tell AccelStepper to move relative to the current position stepper.move(delta); } stepper.run(); } } }; /* modify below this line */ /* define stepper parameters multiple Vid60Stepper instances can share the same StepperConfig object */ struct StepperConfig stepperConfig = { 730, // maxSteps 2200, // maxSpeed 1000 // acceleration }; // define AccelStepper instance AccelStepper stepper(AccelStepper::DRIVER, 4, 5); // define Vid29Stepper class that uses the AccelStepper instance defined in the line above // v-- arbitrary name Vid29Stepper alt100ftPointer(0x107e, // address of stepper data stepper, // name of AccelStepper instance stepperConfig, // StepperConfig struct instance 6, // IR Detector Pin (must be HIGH in zero position) 0, // zero offset [](unsigned int newValue) -> unsigned int { /* this function needs to map newValue to the correct number of steps */ return map(newValue, 65535, 0, 0, stepperConfig.maxSteps - 1); }); void setup() { DcsBios::setup(); } void loop() { DcsBios::loop(); } This was based on some code produced (I think by Craig) some time back. I now want to try a clean sheet version. The IR sensor part works fine, but I would like to see if there is a revised version that works better. So, do any of you have any alternative sketches that I could try? Cheers Les Edited October 15, 2023 by lesthegrngo
Vinc_Vega Posted October 7, 2023 Posted October 7, 2023 Hi Les, we had a long discussion on the altimeter sketch of the Bf-109. Maybe there are some useful points for your investigation. Else we may again discuss details via PM. Best regards, Vinc Regards, Vinc real life: Royal Bavarian Airforce online: VJS-GermanKnights.de [sIGPIC][/sIGPIC]
lesthegrngo Posted October 9, 2023 Author Posted October 9, 2023 The AccelStepper version in that thread worked, once I made a patch PCB to enable me to delete the stepper driver. It is not perfect, despite playing around with the max steps in the stepper config, the position of the pointer doesn't properly match the 'real' position, but it will pass through the zero point with out a step forwards or backwards, so the rotation is smooth and doesn't detract. I also had no issues getting it functioning with RS485 either Puzzlingly the max steps value had to be changed when I went from the USB to the RS485 version, I don't know of any logical reason why that would be. Nonetheless, compared to the old version it is a massive improvement. In case anyone wants to use it here is the final sketch, which includes the fully functioning Baro set and Alt/Elec/Pneu rotary switch //#define DCSBIOS_IRQ_SERIAL #define DCSBIOS_RS485_SLAVE 100 #define TXENABLE_PIN 2 #include <AccelStepper.h> #include "DcsBios.h" struct StepperConfig { unsigned int maxSteps; unsigned int acceleration; unsigned int maxSpeed; }; class Vid60Stepper : public DcsBios::Int16Buffer { private: AccelStepper& stepper; StepperConfig& stepperConfig; inline bool zeroDetected() { return digitalRead(irDetectorPin) == 1; } unsigned int (*map_function)(unsigned int); unsigned char initState; long currentStepperPosition; long lastAccelStepperPosition; unsigned char irDetectorPin; long zeroOffset; bool movingForward; bool lastZeroDetectState; long normalizeStepperPosition(long pos) { if (pos < 0) return pos + stepperConfig.maxSteps; if (pos >= stepperConfig.maxSteps) return pos - stepperConfig.maxSteps; return pos; } void updateCurrentStepperPosition() { // adjust currentStepperPosition to include the distance our stepper motor // was moved since we last updated it long movementSinceLastUpdate = stepper.currentPosition() - lastAccelStepperPosition; currentStepperPosition = normalizeStepperPosition(currentStepperPosition + movementSinceLastUpdate); lastAccelStepperPosition = stepper.currentPosition(); } public: Vid60Stepper(unsigned int address, AccelStepper& stepper, StepperConfig& stepperConfig, unsigned char irDetectorPin, long zeroOffset, unsigned int (*map_function)(unsigned int)) : Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), irDetectorPin(irDetectorPin), zeroOffset(zeroOffset), map_function(map_function), initState(0), currentStepperPosition(0), lastAccelStepperPosition(0) { } virtual void loop() { if (initState == 0) { // not initialized yet pinMode(irDetectorPin, INPUT); stepper.setMaxSpeed(stepperConfig.maxSpeed); stepper.setSpeed(200); initState = 1; } if (initState == 1) { // move off zero if already there so we always get movement on reset // (to verify that the stepper is working) if (zeroDetected()) { stepper.runSpeed(); } else { initState = 2; } } if (initState == 2) { // zeroing if (!zeroDetected()) { stepper.runSpeed(); } else { stepper.setAcceleration(stepperConfig.acceleration); stepper.runToNewPosition(stepper.currentPosition() + zeroOffset); // tell the AccelStepper library that we are at position zero stepper.setCurrentPosition(0); lastAccelStepperPosition = 0; // set stepper acceleration in steps per second per second // (default is zero) stepper.setAcceleration(stepperConfig.acceleration); lastZeroDetectState = true; initState = 3; } } if (initState == 3) { // running normally // recalibrate when passing through zero position bool currentZeroDetectState = zeroDetected(); if (!lastZeroDetectState && currentZeroDetectState && movingForward) { // we have moved from left to right into the 'zero detect window' // and are now at position 0 lastAccelStepperPosition = stepper.currentPosition(); currentStepperPosition = normalizeStepperPosition(zeroOffset); } else if (lastZeroDetectState && !currentZeroDetectState && !movingForward) { // we have moved from right to left out of the 'zero detect window' // and are now at position (maxSteps-1) lastAccelStepperPosition = stepper.currentPosition(); currentStepperPosition = normalizeStepperPosition(stepperConfig.maxSteps + zeroOffset); } lastZeroDetectState = currentZeroDetectState; if (hasUpdatedData()) { // convert data from DCS to a target position expressed as a number of steps long targetPosition = (long)map_function(getData()); updateCurrentStepperPosition(); long delta = targetPosition - currentStepperPosition; // if we would move more than 180 degree counterclockwise, move clockwise instead if (delta < -((long)(stepperConfig.maxSteps/2))) delta += stepperConfig.maxSteps; // if we would move more than 180 degree clockwise, move counterclockwise instead if (delta > (stepperConfig.maxSteps/2)) delta -= (long)stepperConfig.maxSteps; movingForward = (delta >= 0); // tell AccelStepper to move relative to the current position stepper.move(delta); } stepper.run(); } } }; /* modify below this line */ /* define stepper parameters multiple Vid60Stepper instances can share the same StepperConfig object */ struct StepperConfig stepperConfig = { 740, // maxSteps 8000, // acceleration 4000 // maxSpeed }; // define AccelStepper instance AccelStepper stepper1(AccelStepper::FULL4WIRE , 5, 4, 6, 7); // define Vid60Stepper class that uses the AccelStepper instance defined in the line above // v-- arbitrary name Vid60Stepper alt100ftPointer(0x107e, // DCS Bios address For stepper data stepper1, // name of AccelStepper instance stepperConfig, // StepperConfig struct instance 9, // IR Detector Pin (must be HIGH in zero position) 0, // zero offset [](unsigned int newValue) -> unsigned int { /* this function needs to map newValue to the correct number of steps */ return map(newValue, 0, 65535, 0, stepperConfig.maxSteps-1); }); void zero_stepper1(){ stepper1.setMaxSpeed(4000); // maximum speed in steps per second. Must be > 0. stepper1.setAcceleration(8000); // desired acceleration in steps per second per second. Must be > 0.0 stepper1.runToNewPosition(630); // go to the upper end stop delay(250); stepper1.setCurrentPosition(630); // set max steps stepper1.runToNewPosition(0); // go to the lower end stop delay(250); stepper1.setCurrentPosition(0); // set steps to zero } const byte altElectPneuPins[3] = {11, 10, 8}; DcsBios::SwitchMultiPos altElectPneu("ALT_ELECT_PNEU", altElectPneuPins, 3); DcsBios::RotaryEncoder altSetPressure("ALT_SET_PRESSURE", "-1600", "+1600", 12, 13); void setup() { DcsBios::setup(); zero_stepper1(); } void loop() { PORTB |= (1<<5); PORTB &= ~(1<<5); DcsBios::loop(); } cheers Les 1
lesthegrngo Posted October 15, 2023 Author Posted October 15, 2023 A quick update on this I increased the voltage to the IR LED after discussions with the old sketch not seeming to be sensitive enough - you can read all about it here https://forum.dcs.world/topic/318814-increasing-the-sensitivity-of-ir-digital-position-sensor/#comment-5152445 if you are feeling so inclined. My new setup uses a lot of that hardware, and it looks like I may have to go backwards a step on the sensitivity. The new sketch works well, right up until you put the gauge face and 'glass' on it, after which it will not zero. It looks like I have increased the IR LED output to the point where the IR light is bouncing back off the 'glass' with the result that there is no real zeroing signal. Additionally, the potentiometer that is supposed to adjust sensitivity is unable to compensate for it. So, it looks like I'll have to go back to the 390 ohm resistor, by no means a difficult task. Maybe a potentiometer would be an idea so that I can regulate it but I would have to redo the PCB for that Cheers Les
Recommended Posts