Sandwich Dot IO
Hey everyone, what's up?
So here's something super interesting and cool: SANDWICH DOT IO.
It's a Raspberry Pi system with onboard power, a dedicated cooling layer, and even RGB LEDs for aesthetics.
This setup was created to address a problem I encountered while working on a Raspberry Pi-based project:
I wanted to create a small game emulator using the RPI 3B+, but the problem was the power supply. A 5V adaptor was used for powering the Raspberry Pi.
A fan was also required to keep the Pi cool, but I had to add another 5V supply for it as we cannot plug it directly into the 5V of the Raspberry Pi.
So I made this simple system that consists of three layers, each made from FR4 PCB. The first layer, or battery layer, contains a power management IC (IP5306) with four Li-ion cells that give a stable 5V output for running the Pi and other stuff.
The second layer holds the Raspberry Pi in place by using the four mounting holes present on the Pi. I used PCB standoffs to hold the Pi in place.
It also has RGB LEDs (WS2812B) to make this setup look cool and hip; they are powered by the onboard Attiny85 MCU, and the color is controlled by a vertical push-button.
The third layer contains a cooling fan setup; a DC 5V fan is placed right on top of the Raspberry Pi processor and is powered by a proper fan driver made by using a Mosfet IC controlled by an Attiny13A; this concept was reused from one of my previous projects.
Because this setup has active cooling, the Raspberry Pi processor works great, and we could use this setup for power-hungry applications like running an emulation OS and playing DOOM, for example.
This Instructables is about the whole building process of this setup in-depth, so let's get started.
Supplies
Following were the materials used in this built-
- Custom PCBs
- Raspberry Pi Model 3B+ (can use any Pi version)
- DC 5V Fan
- PCB Standoffs
- Attiny85 THT
- Attiny13A SMD
- AO4406 Mosfet IC
- WS2812B LEDs
- JST connectors
- JST wire harness
- Li-ion cell 18650 holder
- Li-ion cells
- IP5306 IC
- 1uF Capacitors
- Type C USB Port
- Normal USB port
- Vertical Switch
- Slide switch ON OFF
- indicator LEDs 0603 package
- SD Card
Concept
Before starting the project, I first prepared a CAD model of the SANDWICH setup that I had in mind.
The battery layer was placed at the bottom because of stability issues.
On top of the battery layer, there will be the Raspberry Pi carrier board. This layer will hold the Raspberry Pi and also act as a power distribution layer as it will contain JST connectors for connecting stuff for powering via 5V fed through the bottom layer.
The middle layer also contains an Attiny85-controlled RGB LED setup for aesthetics.
The third layer, known as the "fan layer," is used to cool the Pi from the top and is blown by the fan on top of the processor. This third layer was completely remodeled from a previous project.
Electrically, it is the same as my previous project; only the form or shape has changed.
After finalizing the model, I marked each layer's dimensions with hole distance and other details and made a few sketches that will be later used in PCB Cad for making outlines and placing components.
This can also be done by exporting an SVG file and importing it into CAD software.
PCB Design
A total of three different PCBs were used in this project; each served different purposes, so various components were used on each board.
Let's start with the base layer, layer 1, or the battery layer, which contains the power source for the whole system.
The IP5306 IC was used in this layer, which is a power management IC that boosts 3.7V from a Li-ion cell to a constant 5V and can provide current up to 2A.
It offers features like battery fuel level LEDs, high-low cutoff features, and thermal protection.
Four Li-ion cells were added to this setup.
Another IP5306 is connected in parallel with the existing IP5306 IC, doubling the output current by adding two power management ICs in parallel.
The second layer is the carrier board, which contains two main parts, the connector array that distributes power from the input side to all connectors and the Attiny85 setup that is connected with 10 WS2812B LEDs.
The third layer is the coolest of all, literally.
It contains an Attiny13A, which drives a simple Mosfet as a switch setup for controlling a load, which is a 5V DC fan.
I took this schematic from a previous fume extractor project and made some changes in the power section, previous fume extractor contained an IP5303 setup, but this one takes 5V input from below the carrier board through a JST connector and uses it as a power source.
After finalizing the schematic, we convert all three designs into three separate layers.
Details of mounting holes, PCB size, holes, and slots were made by following the drawing and dimensions taken from the Cad design.
PCBWAY
After preparing the PCBs for all three boards and exporting their Gerber data, we sent them to PCBWAY for samples and placed an order for a white solder mask with black silkscreen for all three boards.
I received PCBs within a week, and they were excellent, as expected.
White PCBs look great with black silkscreen in general.
Out of three boards, two were simple and symmetrical, but the fan PCB has a whole cutout in the middle with a slot. I love the quality and tooling on the board. There are other manufacturers available, but PCBWAY's service is always on another level.
Check out PCBWay service for getting excellent PCB service at a lower cost.
Battery Layer Assembly
- For the Assembly of the Battery layer, we first start by adding solder paste to each pad of components one by one. We use regular Sn-Pb solder paste that has a melting temperature of 140° to 270°C, and to apply the solder paste, a solder paste syringe with a wide nozzle is used.
- Next, we use an ESD tweaker to carefully pick and place all the SMD components in their assigned places one by one, which took like 30 seconds tops, but the result was a perfect PCB with all the components placed in their locations.
- we carefully lift the whole circuit board and place it on a Reflow Hotplate. The hotplate heats the PCB from below up to the solder paste melting temperature. As a result, the solder paste melts and components get solder on their pads.
- Next, we add THT components to the Board which includes a USB port, Vertical Switch, and Two Dual Lithium cell Holder.
Adding Li-ion Cells to the Holder and Testing
- After completing the Assembly Process of the Battery Layer Board, we add four 18650 Li-ion Cells to the Cell Holder in the right polarity.
- Each cell should have the same voltage so make sure to check their voltage with a multimeter before placing them into the Cell holder.
- We test the Output voltage next by pressing the vertical on-off switch and using a multimeter to measure the voltage across output terminals, 5.1V should be observed.
- at last, we connect a 5V Adaptor through a USB Type C Cable and check if the setup charges or not, fuel LEDs should turn ON when the charger is plugged in.
- The Battery Layer is now completed.
Raspberry Pi Carrier Layer Assembly
Let's start the Raspberry Pi Carrier Board Assembly.
- Like the previous board, we first need to add solder paste to each component pad one by one by using a solder paste dispensing syringe.
- We then pick and place all the SMD components in their assigned places one by one
- Next, we carefully lift the whole circuit board and place it on a Reflow Hotplate.
- We then add THT Components which are the vertical Button, JST connector, slide switch, and DIP8 IC Socket.
- After this, we prepare the ATtiny85 Microcontroller, by preparing i mean adding code in it for Driving LEDs.
Attiny85 Programming
As for the programming process, we cannot directly program the ATTINY85 through a USB. There's a method for programming the Attiny straight from the USB port, but I'm not doing that.
Instead, I'll be using the ISP flashing method, which will utilize the SPI pins of the ATtiny85 to burn the bootloader into it and then upload the main sketch into it.
Getting Attiny85 Core Installed on Arduino IDE
Before starting the flashing process, we first need to download and install the Attiny13 Core files in the Arduino IDE.
https://github.com/SpenceKonde/ATTinyCore
- Open the Arduino IDE.
- Open the File > Preferences menu item.
- Enter the following URL in Additional Boards Manager URLs:
http://drazzy.com/package_drazzy.com_index.json
- Open the Tools > Board > Boards Manager... menu item.
- Wait for the platform indexes to finish downloading.
- Scroll down until you see the MicroCore entry and click on it.
- Click Install.
- After installation is complete close the Boards Manager window.
Preparing the Arduino as ISP setup
AVR chips are typically shipped blank; they must be configured to be Arduino IDE compatible, which requires the use of an AVR programmer, such as a USBASP.
Fun fact: you could make your own AVR programmer with an Arduino Uno or a Nano board in a very easy step.
- Connect your Arduino board with the com port and select the following sketch
- Example>ArduinoISP upload this sketch onto your board
- After uploading, go to the tools menu and choose the Arduino as ISP option in the programmer section.
- Now for flashing Attiny85, we can select Attiny85 in the Board section.
The programming process uses VCC, GND, and four data pins. Three pins connect MISO, MOSI, and SCK between the programming microcontroller and the target. The fourth pin from the programming microcontroller goes to the reset pin of the target device.
Instead of using an Arduino UNO and a breadboard for this job, I will use my DIY Attiny Programmer, which I made for flashing the Attiny or Atmega MCUs.
which you can check out from here- https://www.instructables.com/Multiple-ATtiny8513A-Programmer/
- connect the Board to the Arduino as ISP Setup in the above wiring config
- choose the right port, and right programmer (Arduino as ISP), and hit Burn Bootloader
- wait for few seconds, you will get done burning the bootloader message.
- Now Open the sketch that you want to upload to this Attiny
- Go to the Sketch menu and select Upload using the programmer.
- and your Sketch will get uploaded onto the attiny85.
CODE
#include <Adafruit_NeoPixel.h>
#define BUTTON_PIN 4 // Digital IO pin connected to the button. This will be
// driven with a pull-up resistor so the switch should
// pull the pin to ground momentarily. On a high -> low
// transition the button press logic will execute.
#define PIXEL_PIN 0 // Digital IO pin connected to the NeoPixels.
#define PIXEL_COUNT 10
// Parameter 1 = number of pixels in strip, neopixel stick has 8
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_RGB Pixels are wired for RGB bitstream
// NEO_GRB Pixels are wired for GRB bitstream, correct for neopixel stick
// NEO_KHZ400 400 KHz bitstream (e.g. FLORA pixels)
// NEO_KHZ800 800 KHz bitstream (e.g. High Density LED strip), correct for neopixel stick
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
bool oldState = HIGH;
int showType = 0;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
// Get current button state.
bool newState = digitalRead(BUTTON_PIN);
// Check if state changed from high to low (button press).
if (newState == LOW && oldState == HIGH) {
// Short delay to debounce button.
delay(20);
// Check if button is still low after debounce.
newState = digitalRead(BUTTON_PIN);
if (newState == LOW) {
showType++;
if (showType > 9)
showType=0;
startShow(showType);
}
}
// Set the last button state to the old state.
oldState = newState;
}
void startShow(int i) {
switch(i){
case 0: colorWipe(strip.Color(0, 0, 0), 50); // Black/off
break;
case 1: colorWipe(strip.Color(255, 0, 0), 50); // Red
break;
case 2: colorWipe(strip.Color(0, 255, 0), 50); // Green
break;
case 3: colorWipe(strip.Color(0, 0, 255), 50); // Blue
break;
case 4: theaterChase(strip.Color(127, 127, 127), 50); // White
break;
case 5: theaterChase(strip.Color(127, 0, 0), 50); // Red
break;
case 6: theaterChase(strip.Color( 0, 0, 127), 50); // Blue
break;
case 7: rainbow(20);
break;
case 8: rainbowCycle(20);
break;
case 9: theaterChaseRainbow(50);
break;
}
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
As for the sketch, I'm using Adafruit's Button Cycler sketch, which lets us change the color of LEDs to R, G, and B colors. then run some color animations.
It's pretty basic and does the trick.
Adding Raspberry Pi to the Carrier Board
- Next, we mount the Raspberry Pi to the carrier board by using eight PCB standoffs and four mounting screws.
- Eight standoffs are added to the Raspberry Pi's four mounting holes.
- Later, four screws are used to hold the Raspberry Pi to the carrier board.
- The Carrier board is now completed.
Fan Layer Assembly
- For the Fan Layer Board Assembly, we first add solder paste to each component pad.
- Next, we pick all the SMD components and place them in their place.
- After this, we add the Circuit on an SMT reflow Hotplate for heating up and melting the solder paste.
- After completing the Reflow Process, we add the remaining THT components like the JST connector and vertical switch in their place and solder them using a soldering iron.
- At last, we add a JST wire harness to the input side of the board, this is for connecting the Fan layer with the below Carrier board.
Adding DC Fan to the PCB
Next, we add a 5V, 40mm x 40mm DC fan in its place on the fan layer.
- We first put the DC FAN's wire harness into the JST connector of the Fan layer.
- I have made a central hole for airflow along with four Mounting Holes for adding nuts and bolts to secure the fan in its place.
- I use two hex Bolts with two nuts (both M3) to hold the DC fan in its place.
Fan Layer Code and Programming
The fan layer is driven by the Attiny13A, which is programmed by connecting the same Arduino Nano-based ISP device that I used to program the Attiny85.
We need to download the Attiny13 cores first, which are not included in the Attiny85 core.
- Open the File > Preferences menu item.
- Enter the following URL in Additional Boards Manager URLs: https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json
- Open the Tools > Board > Boards Manager... menu item.
- Wait for the platform indexes to finish downloading.
- Scroll down until you see the MicroCore entry and click on it.
- Click Install.
- After installation is complete close the Boards Manager window.
Here's the main code used in the fan layer.
const int switchPin = 4;
const int FANPin = 0;
int FANMode = 1;
void setup()
{
pinMode(FANPin, OUTPUT);
pinMode(switchPin, INPUT_PULLUP);
digitalWrite(FANPin, LOW);
}
void loop()
{
if (digitalRead(switchPin) ==LOW)
{
FANMode = FANMode + 1;
if (FANMode == 4)
{
FANMode = 1;
}
}
if (FANMode == 1)
{
digitalWrite(FANPin LOW);
delay(200);
}
else if (FANMode == 2)
{
digitalWrite(FANPin, HIGH);
delay(200);
}
else if (FANMode == 3)
{
analogWrite(FANPin, 50);
delay(200);
}
//delay(200); // see text
}
To connect the programmer with Attiny's SPI pins, I added the fan layer's CON6 header pin socket to the male header pins of the Arduino Nano programmer and did the burning bootloader step.
Read more about this step here-
https://www.instructables.com/Overengineered-Fume-Extractor-Project/
Battery Layer Main Assembly
- For the final assembly, we first start by adding eight PCB standoffs to the battery layer and add additional bare PCB to the bottom side of the Battery layer by using four screws tightened into the PCB standoffs. This step is for adding a Base layer which will secure the battery layer from the bottom side.
Four 10mm PCB standoffs were used on the bottom side, and four 35mm standoffs were used on the top side. We will be adding another layer to the 35mm standoffs.
Battery Layer Merge With Raspberry PI Carrier Layer
- The Raspberry Pi Carrier Layer is then attached to the four 35mm PCB Standoffs using four more 35mm PCB Standoffs.
35mm standoffs were used in both layers to make enough room for the components to stay isolated from the top side. This also creates a decent gap for airflow for the Raspberry Pi carrier layer.
Adding Fan Layer
- We now add the Fan layer to the Setup by placing the Fan layer on PCB standoffs and using two more PCB standoffs with two Screws to hold the layer in its place.
I added two small 10mm PCB standoffs to this setup as I'm going to add an additional layer in a future revision.
Result and Working
Here's the result so far, and as you can see, this setup works and is looking pretty sweet, especially the RGB LED part.
The vertical switch on the bottom battery layer powers the entire system, the vertical switch on the carrier layer controls the LED color; and the vertical switch on the fan layer activates the fan.
Three vertical buttons on each layer control their layer-respected function.
Next, we add an OS to the Raspberry Pi and test out a few things.
Booting Recalbox OS
The first OS that I'm booting into the Pi is the Recalbox OS which is an emulator system OS for running retro games.
RecalBox comes preloaded with games and is totally plug-and-play, we can even download custom ROMs and put them into RecalBox and run them.
This OS is easy to install as well, I use the Raspberry Pi imager and choose RecalBox for RPI3 in the Emulation-OS option.
After preparing the SD card, we mount it into the SD card slot of the Raspberry Pi and turn the system on; additionally, we need a keyboard, a mouse, and an HDMI display for testing the system.
Running DOOM
Here's me playing the original DOOM on a Raspberry Pi, How cool is that!
The game is actually operating at excellent quality and is pretty dang playable; there are no lag or time delay or screen freezes issues.
The fan is keeping things cool.
Booting Raspberry Pi OS
Next, we install the Raspberry Pi OS onto another SD card and then put it into the SD Card slot of the Raspberry Pi.
After waiting for a couple of minutes, it boots up, and we can now use this setup for running the Raspberry Pi OS.
Playing Minecraft Pi Edition
Minecraft Pi Edition doesn't come pre-installed, so to install it, we first open the terminal and type the below command.
sudo apt-get update
sudo apt-get install minecraft-pi
Once that finishes, Minecraft Pi and the Python library should be installed
Next, in the terminal, we enter Minecraft-pi, and it will open the Minecraft application.
Conclusion and Further Use
This setup works and is pretty useful if we want to prepare a wire-free setup or something that will run off an onboard power source.
For example, we can set up Pi-hole and use this setup as a power source, or even add a portable display to this setup to make a portable RPI-based computer.
Its use and applications are unlimited.
Forth Layer Idea for the Next Version
For the next version, I will be preparing a new layer, which will be the display layer.
I have a 7-inch HDMI display at my disposal; it's a 5V-powered display with HDMI output, and we can prepare a mounting bracket (using PCB FR4 material) to hold the screen in place and power it up with 5V from the Raspberry Pi Power Board.
Because there will be more power usage for the whole system, which will include a display, fan, LEDs, and Raspberry Pi board, I will increase the number of cells and add yet another bottom layer filled with 4 more cells, so we will have 24000 mAh in total.
This will be for the next article. Thanks for reading this article.
Thanks PCBWAY for supporting this project, you guys can check them out if you need great PCB and stencil service for less cost and great quality.
And I'll be back with a new project pretty soon!