LiPo batteries are a great power source for projects but they require care during use and charging. LiPo batteries can be a fire hazard if not handled properly.
Please read these guidelines on battery handling before you use them!
In this project, students will design and create their own “Fabric Friend” using sewing, circuitry, and coding. Creating a stuffed critter of some kind is a great fabric arts project for students 8 years and up. Once you have sewn the Fabric Friend, you can find a variety of fun and challenging ways to code it to respond to the world.
Making and using a Fabric Friend is great fun for older kids but be sure to keep this toy away from young children and pets since they could be harmed by chewing on or ingesting the battery or other electronic components.
Hours |
---|
2-4 hours – time will vary depending upon the complexity of the creation and the skill level of the creators.
See the Maker Tools and Techniques chapter for more details on materials.
We’re almost ready to plan the sewing and wiring. First, let’s review how the components will need to be connected.
Here is a look at a Flora NeoPixel:
The Circuit Playground Express has three GND (ground) pins to connect to the minus / negative tabs on the NeoPixels:
The Circuit Playground Express has three power pins (two 3.3V and one VOUT) to connect to the plus / positive tabs on the NeoPixels:
The Signal or Data connectors are pins A0 – A7 on the Circuit Playground Express. This diagram shows how you might sew conductive thread from the Circuit Playground Express to NeoPixels:
Sketch your proposed wiring on paper first. Remember that none of the lines of conductive thread can touch each other, so make sure you leave plenty of space between the wires.
Transfer your Wiring Diagram onto the Fabric cutout.
You can use a single line of stitches that begin and end at eyelets on the hardware.
You can also use a double line of stitches. This is more work but makes a more reliable connection.
The end result of either the single line of stitches or a double one should be lines of thread that make strong connections and that don’t touch.
For more on working with conductive thread and NeoPixels see a great video by Becky Stern of Adafruit:
Also, you can read more about it at: Sewing more pixels.
Refer to the Sewing section of the Maker Tools and Techniques chapter for more sewing tips and tricks.
To hide the tail, run the needle and thread through the body of the Fabric Friend. Compress it a little, then snip the thread. The tail will be hidden inside.
See Coding the Circuit Playground Express for more information.
There are several different ideas you use to program you Fabric Friend. We’ll show you two – a Fabric Friend with predictive power, and one that you must feed to keep happy.
First, let’s code a Fabric Friend with predictive powers, kind of like a Magic Eight Ball. We’ll use a randomly chosen variable to select what face the Fabric Friend will make upon the input of ||input:on shake||
. The responses might be positive, negative, or confused. See this video demo:
||variables:VARIABLES||
Toolbox drawer, click on the Make a Variable button to make 3 variables.Left Eye
, Right Eye
, and Magic 8
. ||loops:LOOPS||
Toolbox drawer, drag an ||loops:on start||
block onto the Workspace.||variables:VARIABLES||
Toolbox drawer, drag out two ||variables:set||
blocks, and place them into the ||loops:on start||
block.||variables:set||
blocks, change the variable to Right Eye
.let Right_Eye = 0
let Left_Eye = 0
||light:NEOPIXEL||
Toolbox drawer, drag out two ||light:create strip||
blocks onto the Workspace and place one each into the Left Eye
and Right Eye
||variables:set||
variable blocks replacing the 0
. ||light:create strip||
blocks, set them to the Pins that you will be using to connect to your Flora NeoPixels (for example A1 and A3).||light:create strip||
blocks, change the value of the pixels
to 1
(there is only 1 light on each of your Flora NeoPixels.let Right_Eye: light.NeoPixelStrip = null
let Left_Eye: light.NeoPixelStrip = null
Left_Eye = light.createStrip(pins.A1, 1)
Right_Eye = light.createStrip(pins.A3, 1)
Next, we want to set our lights to a default setting.
||light:LIGHT||
Toolbox drawer, drag a ||light:set all pixels||
block onto the Workspace and place in the ||loops:on start||
block.||light:set all pixels||
block, set a light blue color.||light:NEOPIXEL||
Toolbox drawer, drag out two ||light:strip show animation||
blocks onto the Workspace and place in the ||loops:on start||
block after the ||light:set all pixels||
block.||light:strip show animation||
blocks, select the Left Eye
and Right Eye
variables.||light:strip show animation||
blocks, select the Sparkle animation ||light:strip show animation||
blocks, select 2 seconds (or 2000
milliseconds).let Right_Eye: light.NeoPixelStrip = null
let Left_Eye: light.NeoPixelStrip = null
Left_Eye = light.createStrip(pins.A1, 1)
Right_Eye = light.createStrip(pins.A3, 1)
light.setAll(0x00ffff)
Left_Eye.showAnimation(light.sparkleAnimation, 2000)
Right_Eye.showAnimation(light.sparkleAnimation, 2000)
Next, let’s activate our Creature Prediction by shaking our creature.
||input:INPUT||
Toolbox drawer, drag an ||input:on shake||
block onto the Workspace.||variables:VARIABLES||
Toolbox drawer drag a ||variables:set||
block onto the Workspace and drop into the ||input:on shake||
block.||variables:set||
block, select the Magic 8
variable.||math:MATH||
Toolbox drawer drag a ||math:pick random||
block onto the Workspace and drop into the ||variables:set||
block replacing 0
.||math:pick random||
block, change the values to 1
and 3
, giving you 3 prediction choices.let Magic_8 = 0
input.onGesture(Gesture.Shake, function () {
Magic_8 = Math.randomRange(1, 3)
})
||logic:LOGIC||
Toolbox drawer drag an ||logic:if then else||
block onto the Workspace and drop into the ||input:on shake||
block after the ||variables:set||
block. ||logic:if then else||
block, click the plus (+) icon to add an ||logic:else if||
clause.let Magic_8 = 0
input.onGesture(Gesture.Shake, function () {
Magic_8 = Math.randomRange(1, 3)
if (true) {
} else if (false) {
} else {
}
})
||logic:LOGIC||
Toolbox drawer drag 2 comparison ||logic:0 = 0||
blocks onto the Workspace and place one into the ||logic:if||
clause replacing true, and place the other into the ||logic:else if||
clause.let Magic_8 = 0
input.onGesture(Gesture.Shake, function () {
Magic_8 = Math.randomRange(1, 3)
if (0 == 0) {
} else if (0 == 0) {
} else {
}
})
||variables:VARIABLES||
Toolbox drawer drag out two ||variables:Magic 8||
variable blocks onto the Workspace.||logic:if||
clause comparison block.||logic:else if||
clause comparison block. Note: line up the left edge of the block in the slot where you want to place it. ||logic:if||
clause comparison block, change the value of the second slot to 1
. ||logic:else if||
clause comparison block, change the value of the second slot to 2
.let Magic_8 = 0
input.onGesture(Gesture.Shake, function () {
Magic_8 = Math.randomRange(1, 3)
if (Magic_8 == 1) {
} else if (Magic_8 == 2) {
} else {
}
})
The basic Magic Eight ball responses are ‘Yes’, ‘No’, and ‘Maybe’ so try to design your lights to mean one of these three responses.
||light:LIGHT||
Toolbox drawer drag a ||light:show ring||
block onto the Workspace and drop under the ||logic:if||
clause.||light:show ring||
block (for example a Green Smile).||light:NEOPIXEL||
Toolbox drawer, drag out two |light:strip set all pixels||
blocks onto the Workspace and place after the ||light:show ring||
block.|light:strip set all pixels||
blocks, change the variables to Left Eye
and Right Eye
.||light:show ring||
design (green color for example).|music:MUSIC||
Toolbox drawer drag a |music:play sound|
block onto the Workspace and place after the ||light:Right Eye set all pixels||
block.|music:play sound|
block, select a meaningful sound that corresponds to the prediction you are making.||loops:LOOPS||
Toolbox drawer, drag a ||loops:pause||
block onto the Workspace and place after the |music:play sound|
block.||loops:pause||
block, select 2 seconds (or 2000
milliseconds).let Right_Eye: light.NeoPixelStrip = null
let Left_Eye: light.NeoPixelStrip = null
let Magic_8 = 0
input.onGesture(Gesture.Shake, function () {
Magic_8 = Math.randomRange(1, 3)
if (Magic_8 == 1) {
light.showRing(`black black black black black green green green green green`)
Left_Eye.setAll(0x00ff00)
Right_Eye.setAll(0x00ff00)
music.magicWand.play()
pause(2000)
} else if (Magic_8 == 2) {
} else {
}
})
Now, put the same blocks in the ||logic:else if||
, and ||logic:else||
clauses – but change your light design and sounds to mean “No”, and “Maybe”.
Finally, at the end of our ||input:on shake||
block, let’s reset our creature to the default light settings.
||light:set all pixels||
block and the two ||light:show animation||
blocks in the ||loops:on start||
block to copy those blocks, and place them at the very end of our ||input:on shake||
block.Your final program may look like this:
let Right_Eye: light.NeoPixelStrip = null
let Left_Eye: light.NeoPixelStrip = null
let Magic_8 = 0
input.onGesture(Gesture.Shake, function () {
Magic_8 = Math.randomRange(1, 3)
if (Magic_8 == 1) {
light.showRing(`black black black black black green green green green green`)
Left_Eye.setAll(0x00ff00)
Right_Eye.setAll(0x00ff00)
music.magicWand.play()
pause(2000)
} else if (Magic_8 == 2) {
light.showRing(`purple purple purple purple purple black black black black black`)
Left_Eye.setAll(0x7f00ff)
Right_Eye.setAll(0x7f00ff)
music.powerDown.play()
pause(2000)
} else {
light.showRing(`blue black black black blue orange blue orange orange orange`)
Left_Eye.setAll(0x0000ff)
Right_Eye.setAll(0xff8000)
music.wawawawaa.play()
pause(2000)
}
light.setAll(0x00ffff)
Left_Eye.showAnimation(light.sparkleAnimation, 2000)
Right_Eye.showAnimation(light.sparkleAnimation, 2000)
})
Left_Eye = light.createStrip(pins.A1, 1)
Right_Eye = light.createStrip(pins.A3, 1)
light.setAll(0x00ffff)
Left_Eye.showAnimation(light.sparkleAnimation, 2000)
Right_Eye.showAnimation(light.sparkleAnimation, 2000)
Now let’s code a Fabric Friend who gets hungry and irritable over time, but who can be fed to regain happiness and composure.
||variables:VARIABLES||
Toolbox drawer, click on the Make a Variable button to make 3 variables.Left Eye
, Right Eye
, and Hunger
.||loops:LOOPS||
Toolbox drawer, drag an ||loops:on start||
block onto the Workspace.||variables:VARIABLES||
Toolbox drawer, drag out three ||variables:set||
blocks, and place them into the ||loops:on start||
block.||variables:set||
blocks, change the variables to Left Eye
, Right Eye
, and Hunger
.||light:NEOPIXEL||
Toolbox drawer, drag out two ||light:create strip||
blocks onto the Workspace and place one each into the Left Eye
and Right Eye
||variables:set||
variable blocks replacing the 0
.||light:create strip||
blocks, set them to the Pins that you will be using to connect to your Flora NeoPixels (for example A4 and A7).||light:create strip||
blocks, change the value of the pixels to 1
(there is only 1 light on each of your Flora NeoPixels).let Hunger: number = 0
let Right_Eye: light.NeoPixelStrip = null
let Left_Eye: light.NeoPixelStrip = null
Left_Eye = light.createStrip(pins.A4, 1)
Right_Eye = light.createStrip(pins.A7, 1)
Hunger = 0
Now that we have our variables set up, let’s make sure we can reset our Quadropus’ hunger value.
|input:INPUT||
Toolbox drawer, drag an ||input:on button A click||
block onto the Workspace.||variables:VARIABLES||
Toolbox drawer, drag a ||variables:set||
variable block onto the Workspace and drop into the ||input:on button click||
block.||variables:set||
variable block, use the drop-down menu to select the Hunger
variable.let Hunger: number = 0
let Right_Eye: light.NeoPixelStrip = null
let Left_Eye: light.NeoPixelStrip = null
Left_Eye = light.createStrip(pins.A4, 1)
Right_Eye = light.createStrip(pins.A7, 1)
Hunger = 0
input.buttonA.onEvent(ButtonEvent.Click, function () {
Hunger = 0
})
Next, let’s set the value for Hunger
– when we feed the Quadropus the Hunger value should go down. And we can use the Light sensor on the Circuit Playground Express to tell if we bring food up to the Quadropus’ mouth – the light will get dark.
||loops:LOOPS||
Toolbox drawer, drag a ||loops:forever||
loop onto the Workspace.||logic:LOGIC||
Toolbox drawer, drag three ||logic:if||
blocks onto the Workspace and place them in the ||loops:forever||
loop.||logic:LOGIC||
Toolbox drawer, drag three comparison blocks into the ||logic:if||
blocks.Build your program to look like the following:
let Hunger = 0
forever(function () {
if (input.lightLevel() < 25) {
Hunger += -3
}
if (Hunger > 0) {
Hunger = 0
}
if (Hunger < 0) {
Hunger = 22
}
})
||logic:if||
statement will check if the light level goes beneath 25 (or whatever value works for the room you’re in), then subtract 3
from the Hunger
variable. This way, when you put food to the Fabric Friend’s mouth, you can dim the light hitting the sensor and feed it.||logic:if||
statement checks if the Hunger
variable goes below zero, then ||variables:set Hunger to 0||
. This is useful because otherwise you might have Hunger
become -30 accidentally, and it would take a long time for the variable to get back to positive numbers.||logic:if||
statement prevents the variable from getting too large as time goes on and resets it down to 22
.Now we need to evaluate what to show when our Quadropus is feeling different levels of hunger, and also increase the Hunger
value as time goes on.
||loops:LOOPS||
Toolbox drawer, drag out another ||loops:forever||
loop and drop onto the Workspace ||logic:LOGIC||
drawer, you’ll need an ||logic:if then else||
block, and click on the plus (+) sign to add a blank slot for every facial expression you want your fabric friend to have.||logic:LOGIC||
Toolbox drawer, drag comparison blocks into the ||logic:if then else||
block to evaluate the different levels of hunger.If you want to evaluate a range of values – for example, if Hunger
is between 14
and 18
, then use the Boolean ||logic:and||
block with 2 comparison blocks.
Finally, we need a way for the variable Hunger
to change.
||loops:LOOPS||
Toolbox drawer, drag a ||loops:pause||
block at the end of the ||logic:if then else||
block and set the value to 1 second (1000
ms).||variables:VARIABLES||
Toolbox drawer, drag a ||variables:change||
variable block onto the Workspace and drop after the ||loops:pause||
block.
That way, every second the variable will get bigger. For 13 seconds, the first statement of the ||logic:if then else||
will be true. Then, for seconds 14-17 the second statement will be the true one. And so on.Finally, it’s time to make facial expressions for each of the Fabric Friend’s states of mind. Be sure to pay attention to where the mini USB port and the JST battery connector are on the simulator so you can orient your smiles, frowns, etc. correctly.
||light:LIGHT||
Toolbox drawer, drag ||light:show ring||
blocks to each of the ||logic:if||
, ||logic:else if||
, and ||logic:else||
clauses to set the lights of the Circuit Playground Express.||light:NEOPIXEL||
Toolbox drawer, drag |||light:strip set all pixels||
blocks to each of the ||logic:if||
, ||logic:else if||
, and ||logic:else||
clauses to set the lights of the NeoPixels.Your final program may look like this:
let Right_Eye: light.NeoPixelStrip = null
let Left_Eye: light.NeoPixelStrip = null
let Hunger: number = 0
input.buttonA.onEvent(ButtonEvent.Click, function () {
Hunger = 0
})
Left_Eye = light.createStrip(pins.A4, 1)
Right_Eye = light.createStrip(pins.A7, 1)
Hunger = 0
forever(function () {
if (Hunger < 14) {
light.showRing(`black black black black black orange orange orange orange orange`)
Left_Eye.setAll(0xff8000)
Right_Eye.setAll(0xff8000)
} else if (Hunger >= 14 && Hunger < 18) {
light.showRing(`black black black black black black orange orange orange black`)
Left_Eye.setAll(0xff8000)
Right_Eye.setAll(0xff8000)
pause(100)
Left_Eye.setAll(0xff0000)
Right_Eye.setAll(0xff0000)
pause(100)
Left_Eye.setAll(0xff8000)
Right_Eye.setAll(0xff8000)
} else if (Hunger >= 18 && Hunger < 22) {
light.showRing(`black black black black black blue blue blue blue blue`)
Left_Eye.setAll(0xff0000)
Right_Eye.setAll(0xff0000)
pause(100)
light.showRing(`blue blue blue blue blue black black black black black`)
pause(100)
} else {
music.siren.play()
light.showRing(`blue blue blue blue blue black black black black black`)
Left_Eye.setAll(0x00ff00)
Right_Eye.setAll(0x7f00ff)
pause(Math.randomRange(100, 200))
Left_Eye.setAll(0xff0000)
pause(100)
Right_Eye.setAll(0x007fff)
Left_Eye.setAll(0xffffff)
pause(Math.randomRange(100, 200))
Right_Eye.setAll(0xff00ff)
pause(100)
}
pause(1000)
Hunger += 1
})
forever(function () {
if (input.lightLevel() < 25) {
Hunger += -3
}
if (Hunger < 0) {
Hunger = 0
}
if (Hunger > 30) {
Hunger = 22
}
})
There is a lot of freedom with this project to create your own interactions, light designs, and sound effects. Some ideas might be: