Introduction: OverEngineered Pen Holder 2.0

About: Hello world;

Greetings.

So here's something OverEngineered and different: a Pen Stand or Holder made completely from PCBs and 3D-printed parts.

The goal was to create a pen stand that was entirely different from the typical pen stand. It would have a number of features, such as a room temperature and humidity meter, an onboard light for aesthetics and illumination, and a USB port that could supply 5V to power various devices, including smartphones.

Version 1 of the PenStand I constructed last year was large and heavy; Version 2 is more compact and has an additional ESP8266 Setup that is connected to an SSD1306 OLED and an AHT10 Sensor.

https://www.instructables.com/OverEngineered-Pen-Holder/

This New Setup is a pen holder that measures the temperature and humidity in the room and shows the data on the SSD1306 display to keep us informed about the current room climate.

This Instructables is about how this device was created, so let's get started with the build.

Supplies

The following were the materials used in this build.

  • Custom PCBs Provided by PCBWAY
  • ESP12F Module
  • SSD1306 OLED
  • AHT10 Sensor
  • RED LEDs 5mm
  • IP5306 Power Management IC
  • 1uF 0805 Package Capacitors
  • Li-ion 18650 Cell Holder
  • Li-ion 3.7V 2900mAh Cell
  • 3D-printed parts
  • 10K Resistor
  • 5.6 Ohms 1206 Package Resistor
  • USB Type C Port

Step 1: Concept

First, the 3D model was created in Fusion360, and it consists of four identically sized rectangular Boards arranged in a cuboid shape. To hold these PCBs together, we designed two inside holders with screw mounting holes. We will add screws to keep the PCB connected to the internal holder.

Internal holders are located on both the top and bottom of the penstand; the bottom holder acts as the lid.

The display, ESP configuration, and AHT10 sensor will be located on the front PCB, while the lithium cell and power management IC arrangement will be located on the bottom PCB.

PCBCAD software was used to generate the outline after exporting the PCB shape DWG file.

Step 2: PCB Design

The first step in PCB design is to create a total of three different boards: the power management board, which houses an IP5306 IC with a lithium cell, the side PCB, which only acts as a connection board to connect the front PCB's VCC-GND to the power management board, and the front board, which includes an ESP12F setup with an SSD1306 display and an AHT10 sensor.

The Power Management Board consists of an IP5306 IC with all necessary parts, such as input and output capacitors, an inductor, etc.

The battery has an 18650 SMD holder footprint added, and we used a Type C connector for Power IN. Moreover, 10 red LEDs were added in parallel using four 1206 resistors with the IP5306's 5V, and there is a push switch between the 5V and the positive of the LEDs.

After the power board schematic is complete, we use the DWG file exported from Fusion360 to create the PCB outline and add all the components to the board. We placed LEDs on the bottom side of the PCB, and IP5306 IC components, including the battery holder, were all added on the top side of the PCB as the top side will face outwards. LEDs will be placed inside the pen holder to illuminate the entire assembly from the inside.

As the ESP12F is a 3.3V device and 5V will damage the MCU, we added an AMS1117 Voltage Regulator setup to step down 5V from the input side to 3.3V for ESP to function. Next, we prepare the front PCB, which is the ESP12F setup connected with an SSD1306 OLED and AHT10 sensor. The entire board is powered from 5V we get from the power module board.

Both the display and AHT10 sensor are powered by 5 volts.

The same DWG file is used to create the outline for this board as well. AHT is added on one side, and ESP12F is added below the display in the middle.

The third board, which is the side PCB, will be empty and without any ports or connecting pads, but we have added two CON1 ports with PCB traces linked to them that extend 5V and GND from one side of the board to the other.

In addition to serving this purpose, the side PCB has patterns and a solder mask aperture on both sides that allows light from the LEDs to pass through.

Step 3: PCBWAY

After completing all three PCB Designs, we export their gerber data and send them to PCBWAY for samples.

Three Orders were placed, all for RED Solder mask and white silkscreen.

After placing the order, I received the PCBs within a week, and the PCB quality was pretty great. The silkscreen I used is completely random and asymmetrical, so it's pretty hard to make, but they did an awesome job of making this PCB with no errors whatsoever.

You guys can check out PCBWAY if you want great PCB service at an affordable rate.

Step 4: Front Temp/Humidity PCB Assembly

We begin with the Front TEMP/Humidity PCB Assembly process, which required the use of two soldering techniques because it involved both SMD and THT components.

  • We first apply solder paste to each SMD component pad using a solder paste dispenser.
  • Next, we pick and place all the SMD components in their proper places using an ESD Tweezer.
  • Following the pick-and-place procedure, the PCB is carefully lifted and placed on the SMT Reflow hotplate, which heats the PCB from below to the solder paste melting temperature, permanently soldering all of the components to their pads.
  • We gather all the THT components, including the OLED display and AHT10 sensor module, position them on their pads, and then solder the pads with a standard soldering iron.

Step 5: Power Management Board Assembly

Next, we begin the Power Management Board assembly, which followed a process that was identical to the one used to create the front PCB.

  • We first add solder paste to each SMD component's pads, and then, using an ESD Tweezer, we pick up and arrange each SMD component into position.
  • The PCB is then placed on the SMT Hotplate, where it is reflowed and all of the SMD components are soldered to their pads.
  • The remaining THT parts, including the 5mm THT LED, Push Button, USB Port, and push-on-off switch, are then added.

The Board is now complete.

Step 6: Power Source

The entire system is powered by a 3.7V, 2900mAh 18650 lithium-ion battery, and the ESP12F OLED and LEDs are powered by a reliable 5V output from the IP5306 power management IC.

We insert an 18650 cell into its holder by confirming the polarity, and we then use a multimeter to measure the output voltage, which should be 5 volts; this confirms that the setup is functioning.

Step 7: Temp/Humidity Board Programming With NODEMCU

You might not be aware that any ESP board can be programmed using the NODEMCU Board.

By taking a NODEMCU board and connecting a jumper with its GND Pin and EN Pin, which puts the onboard ESP8266 of the NODEMCU into sleep mode, we may utilize the NODEMCU to program the external ESP12F module.

This enables us to connect an external ESP Board to the NODEMCU; so essentially, we are turning off the ESP board on the NODEMCU and connecting a second ESP Board to a few of its pins.

You can read the article below for additional information about this process.

https://www.instructables.com/Program-ESP8266-With-NodeMCU/

Step 8: CODE

#include <Wire.h>
#include <AHTxx.h>

#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

#define OLED_WIDTH 128
#define OLED_HEIGHT 64

#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT);

float ahtValue; //to store T/RH result

AHTxx aht10(AHTXX_ADDRESS_X38, AHT1x_SENSOR); //sensor address, sensor type


void setup()
{
#if defined(ESP8266)
WiFi.persistent(false); //disable saving wifi config into SDK flash area
WiFi.forceSleepBegin(); //disable AP & station by calling "WiFi.mode(WIFI_OFF)" & put modem to sleep
#endif

display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();

Serial.begin(115200);
Serial.println();

while (aht10.begin() != true) //for ESP-01 use aht10.begin(0, 2);
{
Serial.println(F("AHT1x not connected or fail to load calibration coefficient")); //(F()) save string to flash & keeps dynamic memory free

delay(5000);
}

Serial.println(F("AHT10 OK"));

//Wire.setClock(400000); //experimental I2C speed! 400KHz, default 100KHz
}


void loop()
{
/* DEMO - 1, every temperature or humidity call will read 6-bytes over I2C, total 12-bytes */
Serial.println();
Serial.println(F("DEMO 1: read 12-bytes"));

ahtValue = aht10.readTemperature(); //read 6-bytes via I2C, takes 80 milliseconds

display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(30, 0);
display.println(F("Temp-"));

// Serial.print(F("Temperature...: "));

if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs
{

display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(35, 25);
display.println(ahtValue);
display.display();
// Serial.print(ahtValue);

}
else
{
printStatus(); //print temperature command status

if (aht10.softReset() == true) Serial.println(F("reset success")); //as the last chance to make it alive
else Serial.println(F("reset failed"));
}

delay(2000); //measurement with high frequency leads to heating of the sensor, see NOTE

ahtValue = aht10.readHumidity(); //read another 6-bytes via I2C, takes 80 milliseconds

display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(30, 0);
display.println(F("Humd-"));


// Serial.print(F("Humd-"));

if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs
{

display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(35, 25);
display.println(ahtValue);
display.display();


// Serial.println(F(" +-2%"));
}
else
{
printStatus(); //print humidity command status
}

delay(2000); //measurement with high frequency leads to heating of the sensor, see NOTE

/* DEMO - 2, temperature call will read 6-bytes via I2C, humidity will use same 6-bytes */
Serial.println();
Serial.println(F("DEMO 2: read 6-byte"));

ahtValue = aht10.readTemperature(); //read 6-bytes via I2C, takes 80 milliseconds

Serial.print(F("Temperature: "));

if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs
{
Serial.print(ahtValue);
Serial.println(F(" +-0.3C"));
}
else
{
printStatus(); //print temperature command status
}

ahtValue = aht10.readHumidity(AHTXX_USE_READ_DATA); //use 6-bytes from temperature reading, takes zero milliseconds!!!

Serial.print(F("Humidity...: "));

if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs
{
Serial.print(ahtValue);
Serial.println(F(" +-2%"));
}
else
{
printStatus(); //print temperature command status not humidity!!! RH measurement use same 6-bytes from T measurement
}

delay(10000); //recomended polling frequency 8sec..30sec
}

void printStatus()
{
switch (aht10.getStatus())
{
case AHTXX_NO_ERROR:
Serial.println(F("no error"));
break;

case AHTXX_BUSY_ERROR:
Serial.println(F("sensor busy, increase polling time"));
break;

case AHTXX_ACK_ERROR:
Serial.println(F("sensor didn't return ACK, not connected, broken, long wires (reduce speed), bus locked by slave (increase stretch limit)"));
break;

case AHTXX_DATA_ERROR:
Serial.println(F("received data smaller than expected, not connected, broken, long wires (reduce speed), bus locked by slave (increase stretch limit)"));
break;

case AHTXX_CRC8_ERROR:
Serial.println(F("computed CRC8 not match received CRC8, this feature supported only by AHT2x sensors"));
break;

default:
Serial.println(F("unknown status"));
break;
}
}


This code uses the AHT10 library to read temperature and humidity values from an AHT10 sensor and display them on an SSD1306 OLED display.

Let's go through the code step by step:

  • The code includes the necessary libraries for the AHTxx sensor (AHTxx.h), the OLED display (Adafruit_SSD1306.h and Adafruit_GFX.h), and the Wire library for I2C communication.

The code defines constants for the OLED display width, height, and I2C address.

  • The code defines constants for the OLED display width, height, and I2C address.
  • An instance of the Adafruit_SSD1306 class is created, named "display, " with the specified width and height.
  • A floating-point variable "ahtValue" is declared to store the temperature and humidity values.
  • An instance of the AHTxx class is created, named "aht10, " with the specified I2C address and sensor type.

In the setup() function:

  • WiFi-related configuration (for the ESP8266) is disabled.
  • The OLED display is initialized.
  • Serial communication is started.
  • The AHT10 sensor is initialized using the begin() function. It waits until the sensor is connected and the calibration coefficients are loaded.

In the loop() function:

  • DEMO 1: Temperature measurement is performed by calling the readTemperature() function, which returns the temperature value in Celsius. The result is stored in "ahtValue."
  • The OLED display is cleared, and the temperature value is printed on the display using the display library functions.
  • If the temperature value is valid (not equal to AHTXX_ERROR), it is displayed on the OLED display. Otherwise, the printStatus() function is called to print the status of the temperature command and perform a soft reset if necessary.
  • A delay of 2000 milliseconds is added between temperature and humidity measurements.
  • Humidity measurement is performed by calling the readHumidity() function, which returns the relative humidity value in percentage. The result is stored in "ahtValue."
  • The OLED display is cleared, and the humidity value is printed on the display using the display library functions.
  • If the humidity value is valid, it is displayed on the OLED display. Otherwise, the printStatus() function is called to print the status of the humidity command.
  • A delay of 2000 milliseconds is added before moving on to DEMO 2.
  • DEMO 2: Temperature measurement is performed again using the readTemperature() function, and the result is stored in "ahtValue.
  • If the temperature value is valid, it is printed on the serial monitor along with an accuracy range.
  • Humidity measurement is performed using the readHumidity() function, specifying AHTXX_USE_READ_DATA. This reuses the 6-byte data previously read during the temperature measurement, resulting in faster execution.
  • If the humidity value is valid, it is printed on the serial monitor along with an accuracy range.
  • A delay of 10000 milliseconds is added before repeating the loop.

In the loop() function:

  • DEMO 1: Temperature measurement is performed by calling the readTemperature() function, which returns the temperature value in Celsius. The result is stored in "ahtValue."
  • The OLED display is cleared, and the temperature value is printed on the display using the display library functions.
  • If the temperature value is valid (not equal to AHTXX_ERROR), it is displayed on the OLED display. Otherwise, the printStatus() function is called to print the status of the temperature command and perform a soft reset if necessary.
  • A delay of 2000 milliseconds is added between temperature and humidity measurements.
  • Humidity measurement is performed by calling the readHumidity() function, which returns the relative humidity value in percentage. The result is stored in "ahtValue."
  • The OLED display is cleared, and the humidity value is printed on the display using the display library functions.
  • If the humidity value is valid, it is displayed on the OLED display. Otherwise, the printStatus() function is called to print the status of the humidity command.
  • A delay of 2000 milliseconds is added before moving on to DEMO 2.
  • DEMO 2: Temperature measurement is performed again using the readTemperature() function, and the result is stored in "ahtValue."
  • If the temperature value is valid, it is printed on the serial monitor along with an accuracy range.
  • Humidity measurement is performed using the readHumidity() function, specifying AHTXX_USE_READ_DATA. This reuses the 6-byte data previously read during the temperature measurement, resulting in faster execution.
  • If the humidity value is valid, it is printed on the serial monitor along with an accuracy range
  • A delay of 10000 milliseconds is added before repeating the loop.
  • The printStatus() function is defined to print the status of the AHT10 sensor. It checks the status returned by the getStatus() function and prints an appropriate message based on the status code.

Overall, this code sets up the OLED display, initializes the AHT10 sensor, reads temperature and humidity values using the AHTxx library, and displays the values on the OLED display and serial monitor.

Step 9: Final Assembly

The final assembly method begins by first gathering the four Boards, the bottom lid, and the 3D-printed holder together.

  • We begin by using M2 screws to attach the front PCB to the 3D-printed lid before adding the back PCB and the 3D Holder on the top side.
  • We then install side panels using the same M2 screws.
  • After completing the mechanical assembly, we link the power management board's positive and negative terminals to the side PCB using jumper leads. Then, the positive and negative terminals of the side PCB are connected to the front PCB using the same jumper leads.

The Project is now complete.

Step 10: RESULT

This project's final result is an overengineered pen stand holder with an ESP12F setup at the front, an OLED screen, an AHT10 sensor for monitoring room temperature and humidity, along with RED light that lights up the entire thing.

This project was a follow-up to my earlier PEN HOLDER project, which was also made entirely of PCBs.

Ultimately, this project demonstrates that FR4 may be used to create body parts in addition to electronics.

Thanks for getting this far; please leave a comment if you need any help with this project.

Special thanks to PCBWAY for supporting this project; do check them out for great PCB service at a lower cost.

Thanks again, and I will be back with a new project soon.

Unusual Uses Contest

Participated in the
Unusual Uses Contest