Introduction: Split Flap Display - Antique Remix
I've always wanted to play around with a split flap display but haven't until now been able to get one working satisfactorily, The other is that many builds also use laser cut items to which I dont have access. So when this one came along from Jonas Bonjeanand which I discovered on https://hackaday.io/project/163725-split-flap-disp...
I built up one of these, but also wanted to build self-contained electronics into it and also had this idea to make it look antique.
Step 1: Step 1 - the Build Files
Starting with the base models I went ahead and remixed nearly all of the parts, apart from the flaps. As well as building in an enclosure for the Wemos board I also added somewhere to mount the ULN2003 drive board.
So what you see here are some test builds based on a mix of the original parts and some modified components. The bearings used here are 625ZZ which are 16mm OD and 5mm thick with an inner bore diameter of 5mm. Nice little alternatives to the normal go to fidget/skateboard bearing. You can get them cheap as well in 10 packs.
The end plate was remodelled to mirror the other side and the gears were reworked to give a bit more industrial feel to them. Likewise a faux petrol tank was added to cover the mechanical end-stop board and a lifting eye added to provide some detail for the back. There was some internal work also done for the drum to mate easier onto the shaft and a 2mm bolt added to allow the drum to be securely clamped when the flaps were in .
All the latest files can be found on https://www.thingiverse.com/thing:3449562
Attachments
Step 2: Step 2 : Building the Machine
Putting it all together is pretty simple, which is one of things that attracted me to this design. There are a couple of parts where you need to assemble them correctly. The main one of course is the drum. You'll need to add the spacer inside so that it keeps the nub of the drum aligned with the mechanical endstop. The other is to get all the flaps installed without them falling on the floor. I did try and build an assembly jig, but couldnt wait in the end and just used a lot of blu-tak to hold the flaps in place. With them all aligned the same way, the top goes on and a 2mm bolt holds it all together.
Shortening the leads on the stepper motor and on the switch is also needed to keep things nice and neat. Pieces of heatshrink on every lead for this stops anything shorting out and keep all the leads short for the Wemos as well.
Step 3: Step 3: Software
The software is for the Wemos D1 Mini R2 and works on an arduino if you remove the wifi parts. The principle behind this is to place the sensing of the end stop switch on an interupt which zeros the stepper position. You can then move the drum each time to the position you want knowing that zero is always the same. To work out the steps required for each flap considering the number of steps the standard motors have and the gear train I just did it backwards. So I put it all together and just ran the stepper counting the steps for several revolutions to get a sort of average and then divided this by 12. It sort of worked out about right at 225 steps per flap. Then to guarantee that if you wanted to go to the home position, you drive the stepper past this to 2800 steps knowing that it will hit the home switch before that and get zeroed. The only other major issue is that i discovered two different wiring setups for these stepper motors. Older ones I had would go the other way and on inspection you can see that the 5 wires to the motor were in a different order. However all the recent ones I have work with this code.
I also use the Adafruit IO service with this which in turn is fed data from IFTTT. You can use the core and just feed anything in you want, or you can even control it manually through the serial programming port by typing the number of steps you want, or use m1,m2 etc to move directly to a flap position for testing.
Code:-
// Split flap controller using Wemos D1 R1 with 28BYJ-48 stepper motor /************************** Configuration ***********************************/ // Adafruit IO connection details #define IO_USERNAME "io-username-here" #define IO_KEY "io-key-here" // leave blank as we get these through the Wifi Manager when it runs #define WIFI_SSID "" #define WIFI_PASS "" // These set up all the includes to run the wifi component and setup manager #include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino #include <DNSServer.h> #include <ESP8266WebServer.h> #include "WiFiManager.h" //https://github.com/tzapu/WiFiManager #include <WiFiUdp.h> // Adafruit queue service, where we send the IFTT data to and then can download it #include "AdafruitIO_WiFi.h" AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS); // Stepper driver library #include <AccelStepper.h> #define HALFSTEP 8#define HALFSTEP 8 // Wemos values to make addressing the pins easy static const uint8_t D0 = 16; static const uint8_t D1 = 5; static const uint8_t D2 = 4; static const uint8_t D3 = 0; static const uint8_t D4 = 2; static const uint8_t D5 = 14; static const uint8_t D6 = 12; static const uint8_t D7 = 13; static const uint8_t D8 = 15; // Connection pins for stepper controller #define motorPin1 D5 // Driver board IN1 #define motorPin2 D6 // Driver board IN2 #define motorPin3 D7 // Driver board IN3 #define motorPin4 D8 // Driver board IN4 // Define the Pins used for the Home detection #define home_switch D3 // Pin 2 connected to Home Switch (MicroSwitch) - not the best pin as it stops program upload if its not open int incomingByte = 0; // for incoming serial data int flacker = 0; // count for which flap is flacked int carryover = 0; // value to carry over // read position required const byte numChars = 6; char receivedChars[numChars]; // an array to store the received data boolean newData = false; // Homing startup value long initial_homing=-1; // Used to Home Stepper at startup // Define a stepper and the pins it will use AccelStepper stepper1(8,motorPin1,motorPin3,motorPin2,motorPin4); // set up the 'twitterfan' feed , the feed we connect to so as to get updates AdafruitIO_Feed *twitter = io.feed("twitterfan"); // Saves the last tweet details and status so we only fire once String lasttweet =""; bool newTweet = false; int tweeter_number; // All the setup details here void setup() { Serial.begin(115200); Serial.println("split flap drive - 225 steps a flap"); // for testing show the status of the WiFi WiFi.printDiag(Serial); WiFiManager wifiManager; //set a wifi callback that is called if connecting to a previous WiFi fails, this then calls up Access Point setup wifiManager.setAPCallback(configModeCallback); // reset saved settings , un comment this next line to force the testing of the WiFi Manager so you can connect // use your phone or tablet to look for the Split flap network that will appear // wifiManager.resetSettings(); // sets timeout until configuration portal gets turned off // useful to make it all retry or go to sleep in 240 seconds // this means you have a few minutes to connect wifiManager.setTimeout(240); if (!wifiManager.autoConnect("Split Flap")) { Serial.println(F("failed to connect and hit timeout")); //reset and try again delay(3000); ESP.reset(); delay(1000); } // now we connect to the Adafruiti IO service Serial.print(F("Connecting to Adafruit IO")); io.connect(); // set up the message handler for the 'twitterfan' feed. twitter->onMessage(twitterMessage); // wait for a connection while (io.status() < AIO_CONNECTED) { Serial.print(F(".")); Serial.print("The Stepper is now Homing . . . . . . . . . . . "); // Set up the home position switch pinMode(home_switch, INPUT_PULLUP); // Attach an interrupt to the ISR vector for the home switch attachInterrupt(digitalPinToInterrupt(home_switch), handleInterrupt, FALLING); ESP.wdtDisable(); // the timeout has to be turned off as the stepper move is blocking stepper1.setMaxSpeed(750.0); stepper1.move(1); // seems to like a small move to get things going stepper1.setAcceleration(12000); stepper1.setSpeed(50); stepper1.move(2000); ESP.wdtEnable(1000); // so we turn it back on after the movement ESP.wdtDisable(); // the timeout has to be turned off as the stepper move is blocking while (digitalRead(home_switch)) { // Make the Stepper move until the home switch is activated stepper1.moveTo(initial_homing); // Set the position to move to initial_homing++; // Decrease by 1 for next move if needed stepper1.run(); // Start moving the stepper //// Serial.print("."); delay(2); } ESP.wdtEnable(1000); // so we turn it back on after the movement stepper1.setCurrentPosition(0); Serial.print(" Stepper is now Homed position="); Serial.println(stepper1.currentPosition()); delay(500); } // now we are connected Serial.println(); Serial.println(io.statusText()); }//--(end setup )--- void loop() { io.run(); recvWithEndMarker(); // check the serial port for manual step details showNewData(); flapToTweeter(); // Need to call this every loop } // read the serial input and if there is data, store it void recvWithEndMarker() { static byte ndx = 0; char endMarker = '\n'; char rc; // if (Serial.available() > 0) { while (Serial.available() > 0 && newData == false) { rc = Serial.read(); if (rc != endMarker) { receivedChars[ndx] = rc; ndx++; if (ndx >= numChars) { ndx = numChars - 1; } } else { receivedChars[ndx] = '\0'; // terminate the string ndx = 0; newData = true; } } } // move the stepper if we have incoming serial command void showNewData() { if (newData == true) { flacker++; Serial.print("Serial cmd ... rx="); String cmd = String(receivedChars); Serial.print(cmd); Serial.print(' '); if (receivedChars[0] == 'm') { // denotes a move command m1,m2,m3 etc int flap = atoi(&receivedChars[1]); // int flap_pos = flap * 225 * -1; // use this for reverse wired stepper motors that exist int flap_pos = flap * 225; Serial.print(" move flaps from position "); Serial.print(stepper1.currentPosition()); Serial.print (" to "); Serial.print (flap_pos); if (stepper1.currentPosition()<flap_pos) { Serial.println(" move straight there as its before the home position "); stepper1.moveTo(flap_pos); carryover = 0; // 0 value as we can get there in one move } else { Serial.println(" has to go past home "); carryover = flap_pos; stepper1.moveTo(2800); // move to the home position and then the carryover will move it again // we do this so that the stepping always goes in one direction } ESP.wdtDisable(); // the timeout has to be turned off as the stepper move is blocking stepper1.runToPosition(); // if the watchdog timer is enabled, then the wemos will reset ESP.wdtEnable(1000); // so we turn it back on after the movement flacker=flap; } else { //Serial.print(receivedChars); // show what came in on the serial int pos = atoi(&receivedChars[0]); // turn it into an integer and just move there stepper1.move(pos); ESP.wdtDisable(); stepper1.runToPosition(); ESP.wdtEnable(1000); Serial.print(" showing flap "); Serial.print(flacker); Serial.print(" at step position "); Serial.println(stepper1.currentPosition()); } newData = false; for( int i = 0; i < sizeof(receivedChars); ++i ) receivedChars[i] = (char)0; } } // movement to a flap from a io number void flapToTweeter () { if (newTweet == true) { //int flap_pos = tweeter_number * 225 * -1; // use for old reversed steppers int flap_pos = tweeter_number * 225; Serial.print("IO flap move request from step "); Serial.print(stepper1.currentPosition()); Serial.print (" to step "); Serial.print (tweeter_number); if (stepper1.currentPosition()<flap_pos) { Serial.println(" go straight there"); stepper1.moveTo(flap_pos); carryover = 0; } else { Serial.println(" go past home first "); carryover = flap_pos; stepper1.moveTo(2800); } ESP.wdtDisable(); stepper1.runToPosition(); ESP.wdtEnable(1000); flacker=flap_pos; newTweet = false; } } // ----------------------------------------------------------------------------------------- // the interupt when the home switch has been tripped void handleInterrupt() { // Serial.print(" home position="); // Serial.print(stepper1.currentPosition()); // Serial.print(" carry over="); // Serial.println(carryover); stepper1.setCurrentPosition(0); flacker =0; stepper1.moveTo(carryover); // keep moving if a carry over was required ESP.wdtDisable(); stepper1.runToPosition(); ESP.wdtEnable(1000); carryover=0; } // ----------------------------------------------------------------------------------------- // this is called whenever a 'twitter' message arrives - get the first integer value void twitterMessage(AdafruitIO_Data *data) { String tweeter = (data->toString()); tweeter.toLowerCase(); if ((lasttweet != tweeter) && (tweeter != "")) { lasttweet = tweeter; tweeter_number = (data->toInt()); newTweet = true; Serial.print(F("Tweet: ")); Serial.print(tweeter); Serial.println(""); } } // ----------------------------------------------------------------------------------------- // this is called when WiFiManager enters configuration mode, void configModeCallback (WiFiManager *myWiFiManager) { Serial.println(F("Entered config mode")); Serial.println(WiFi.softAPIP()); //if you used auto generated SSID, print it Serial.println(myWiFiManager->getConfigPortalSSID()); }
Step 4: Step 4: Weathering
Not wanting a plain print finish I took the route of trying to give it an antique metal look. You'll also see that I resprayed the motor can as well with some copper airbrush paint from Vallejo. The actual parts themselves were all first painted in Matt black car spray. Then I brushed on a burnt umber and let it dry which right away gives a metalled tone to the colour. This was just brushed on and wiped back, then followed up with some raw umber. These are cheap acrylics from a local art shop and only cost a couple of pounds each. With the petrol tank, this was painted in green and tamiya gold leaf X-12 was painted on. This also got a spray of gloss varnish to give it that oily look that you'd expect with a petrol/oil tank. Last touch was some raw sienna to give a light rusting look and then some silver leaf rub n buff on edges to give the impression of worn metal surfaces.
Step 5: Step 5: Gallery
Here are some pictures I've taken of the final model , I've also added victorian water colour prints of flowers to each flap.