├── images
├── Circuit.png
├── nodemcu_devkit.jpg
└── ikea-ps-pendant-lamp.JPG
├── README.md
└── nodemcu
└── nodemcu.ino
/images/Circuit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biagiobotticelli/IKEA_DIY_Lamp/HEAD/images/Circuit.png
--------------------------------------------------------------------------------
/images/nodemcu_devkit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biagiobotticelli/IKEA_DIY_Lamp/HEAD/images/nodemcu_devkit.jpg
--------------------------------------------------------------------------------
/images/ikea-ps-pendant-lamp.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biagiobotticelli/IKEA_DIY_Lamp/HEAD/images/ikea-ps-pendant-lamp.JPG
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # IKEA PS 2014 DIY Lamp
2 | During a visit in an IKEA store, I saw the PS 2014 lamp. It's a wonderful manually expandable lamp whose movement was controlled by a twine...
3 |
4 |
5 |
6 | Since I'm a Computer Science Engineer, the first thing that I thought was to find a solution to add remote control!!
7 |
8 | The first step that I made was to hack the hardware: to do this, I replaced the string with a **stepper motor** with a threaded rod. Then I added a **microcontroller**: in particular, I've used a **NodeMCU ESP8266** that is an *Open-source*, *Interactive*, *Programmable*, *Low cost* and *WI-FI enabled* integrated development kit.
9 | It can be easily configured and manipulated using the Arduino IDE and it allows to reduce the time to prototype the hardware.
10 |
11 |
12 |
13 | At the end of the configuration of the harware, I have the **circuit diagram** that can be represented as:
14 |
15 |
16 |
17 |
18 | The last thing to do was to design a **firmware** for the NodeMCU board, using the *Arduino IDE*.
19 | The two critical parts were:
20 | 1. *How to control the lamp;*
21 | 2. *How to interact with the stepper motor.*
22 |
23 | #### How to control the lamp
24 | Since the **NodeMCU** boards allows to connect the lamp to my home Wi-Fi, I decided to interact with it using the **Blynk** mobile application (it's available both for Android and iOS).
25 | **Blynk** is extremely easy to use with Arduino IDE: once downloaded the *Blynk Library*, to connect the device to a network requires only 3 fields: the *Authentication Token* (given by the mobile app), the *Wi-Fi SSID* and the *Wi-Fi Password*.
26 | A very *simple sketch* could be:
27 | ```
28 | #include
29 | #include
30 |
31 | char auth[] = "Your-Auth-Token";
32 | char ssid[] = "Your-WiFi-SSID";
33 | char pass[] = "Your-WiFi-Password";
34 |
35 | void setup() {
36 | Serial.begin(9600);
37 | Blynk.begin(auth, ssid, pass);
38 | }
39 |
40 | void loop() {
41 | Blynk.run();
42 | }
43 | ```
44 |
45 | #### How to interact with the stepper motor
46 | In order to *open/close* the lamp, I need to control the stepper motor.
47 | To do this, I've chosen the **AccelStepper** library which is an improvement of the basic *Arduino Stepper Library* since it supports *acceleration* and *deceleration*, a large variety of *stepper motors* and it also supports the *DRV8825 Driver Board* that I've chosen for the project.
48 |
49 | ## Installation Instructions
50 | The code is a *ready-to-work firmware* **ONLY IF** the project is **implemented _exactly_ as the circuit diagram**.
51 | But it can be easy adapted according to the subjective purpose.
52 | To install the firmware, simply download it, open it using the Arduino IDE and load it into the NodeMCU board.
53 |
54 | ## Demo on YouTube
55 | Click on image:
56 |
57 |
60 |
61 |
62 | ## Useful Links
63 | - My personal LinkedIn Profile;
64 | - NodeMCU Documentation;
65 | - Arduino website;
66 | - Blynk Web Page;
67 | - AccelStepper Library Website.
68 |
69 |
--------------------------------------------------------------------------------
/nodemcu/nodemcu.ino:
--------------------------------------------------------------------------------
1 | //#define BLYNK_PRINT Serial // Comment this out to disable prints and save space
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 |
12 | // Auth Token
13 | char auth[] = "Your_Auth_Token";
14 |
15 | // WiFi credentials
16 | char ssid[] = "Your_WiFi_SSID";
17 | char pass[] = "Your_WiFi_PSW";
18 |
19 | // Pins for Stepper Control
20 | const int sleepPin = D0;
21 | const int stepPin = D1;
22 | const int dirPin = D2;
23 |
24 | // Pin for the Relay
25 | const int relayPin = D4;
26 |
27 | // Pin for the LED
28 | const int ledPin = D6;
29 |
30 | // Max Speed and Speed of the Stepper
31 | int sp = 750;
32 | int accel = 80000;
33 |
34 | // MIN and MAX values for Lamp Gap control
35 | const int MIN = 0;
36 | const int MAX = 2400;
37 | int current = 0;
38 | int prom_pos = 0;
39 | int light = 0;
40 |
41 | // Timer
42 | SimpleTimer timer;
43 | int t = 1700;
44 |
45 | // Sectors of flash memory where to store the current position
46 | #define H_ADDRESS 500
47 | #define L_ADDRESS 501
48 | #define LIGHT_ADDRESS 502
49 |
50 | // Flag
51 | bool isFirstConnect = true;
52 | bool isOn = false;
53 | bool isReady = false;
54 |
55 | // Stepper Definition
56 | AccelStepper stepper(1, stepPin, dirPin);
57 |
58 | void readData() {
59 |
60 | // Retrieve from EEPROM the position
61 | byte high = EEPROM.read(H_ADDRESS);
62 | byte low = EEPROM.read(L_ADDRESS);
63 | prom_pos = word(high,low);
64 |
65 | }
66 |
67 | void saveData() {
68 |
69 | // Write the position to EEPROM
70 | EEPROM.write(H_ADDRESS,highByte(current));
71 | EEPROM.write(L_ADDRESS,lowByte(current));
72 | EEPROM.commit();
73 |
74 | }
75 |
76 | void moveLamp(int pos) {
77 |
78 | // Turn ON the Stepper
79 | digitalWrite(sleepPin, HIGH);
80 |
81 | if (pos == MAX) {
82 | pos = MAX-15;
83 | }
84 | else if (pos == MIN) {
85 | pos = MIN+10;
86 | }
87 | stepper.moveTo(pos);
88 | while (stepper.currentPosition() != pos) {
89 | stepper.run();
90 | }
91 |
92 | // Turn OFF the Stepper
93 | digitalWrite(sleepPin, LOW);
94 |
95 | }
96 |
97 | void moveOn() {
98 | int in = current;
99 | if (in >= MAX) {
100 | in = MAX-15;
101 | }
102 | if(current <= MAX) {
103 |
104 | if (current<=1600) {
105 | moveLamp(in+800);
106 | current = in+800;
107 | }
108 | else {
109 | moveLamp(in);
110 | current = MAX;
111 | }
112 | saveData();
113 | Blynk.virtualWrite(V0, current);
114 | }
115 | if(current == MAX) {
116 | isReady = true;
117 | }
118 | }
119 |
120 | void moveOff() {
121 |
122 | int in = current;
123 | if(in != MIN) {
124 | if(in > 800) {
125 | moveLamp(in-800);
126 | current = in-800;
127 | }
128 | else {
129 | moveLamp(MIN+10);
130 | current = MIN;
131 | }
132 | saveData();
133 | Blynk.virtualWrite(V0, current);
134 | }
135 |
136 | }
137 |
138 | void setup() {
139 |
140 | //Serial.begin(115200);
141 | EEPROM.begin(512);
142 |
143 | // Begin the Blynk session
144 | Blynk.begin(auth, ssid, pass);
145 |
146 | // Put the Stepper in SLEEP mode
147 | pinMode(sleepPin, OUTPUT);
148 | digitalWrite(sleepPin, LOW);
149 |
150 | // Turn off the Wi-Fi LED
151 | pinMode(ledPin, OUTPUT);
152 | digitalWrite(ledPin, LOW);
153 |
154 | // Turn ON and OPEN the Lamp as DEFAULT
155 | pinMode(relayPin, OUTPUT);
156 | digitalWrite(relayPin, HIGH);
157 | Blynk.virtualWrite(V1,1);
158 |
159 | if(Blynk.connected()) {
160 | // Lamp is connected -> LED ON
161 | digitalWrite(ledPin, HIGH);
162 | }
163 |
164 | // Read position from EEPROM
165 | readData();
166 |
167 | // Set correct position
168 | stepper.setCurrentPosition(prom_pos);
169 | current = prom_pos;
170 |
171 | // Set Stepper Speed and Acceleration
172 | stepper.setMaxSpeed(sp);
173 | stepper.setSpeed(sp);
174 | stepper.setAcceleration(accel);
175 |
176 | // Open the Lamp
177 | if(current != MAX) {
178 | timer.setTimer(t, moveOn, 3);
179 | }
180 |
181 | }
182 |
183 | // Sync in case of disconnection
184 | BLYNK_CONNECTED() {
185 | if (isFirstConnect) {
186 | Blynk.syncAll();
187 | isFirstConnect = false;
188 | }
189 | }
190 |
191 | // The STEPPER is connected to VIRTUAL_PIN_0 of Blynk App
192 | BLYNK_WRITE(V0) {
193 | // Allow the stepper only if the lamp is on
194 | if (isOn) {
195 | // read the value of STEPPER
196 | int input = param.asInt();
197 |
198 | moveLamp(input);
199 | current = input;
200 | saveData();
201 | }
202 | else {
203 | Blynk.virtualWrite(V0, current);
204 | }
205 | }
206 |
207 | // The BUTTON is connected to VIRTUAL_PIN_1 of Blynk App
208 | BLYNK_WRITE(V1) {
209 |
210 | // read the value of button
211 | int l = param.asInt();
212 |
213 | // Light -> ON
214 | if(l == 1) {
215 | // Turn ON
216 | digitalWrite(relayPin, HIGH);
217 |
218 | // Read position from EEPROM
219 | readData();
220 |
221 | current = prom_pos;
222 | if(current != MAX) {
223 | timer.setTimer(t, moveOn, 3);
224 | }
225 | isOn = true;
226 | }
227 |
228 | // Light -> OFF
229 | else if (l==0 && isReady){
230 | // Turn OFF
231 | digitalWrite(relayPin, LOW);
232 | isOn = false;
233 |
234 | // CLOSE the Lamp
235 | if(current != MIN) {
236 | timer.setTimer(t, moveOff, 3);
237 | }
238 |
239 | }
240 |
241 | }
242 |
243 | void loop() {
244 | Blynk.run();
245 | timer.run();
246 | }
247 |
--------------------------------------------------------------------------------