Today, with 3D printing at our fingertips, anyone can bring their favorite creatures and artworks to life. Amidst countless projects, we couldn't help but notice the adorable Octocat from GitHub. Its endearing cat-like features caught our eye, but it was the tentacles that truly fascinated us. We envisioned soft, flexible tentacles, just like the real deal. So, we got to work, pouring silicone for the tentacles and using pneumatic valves and the Infineon DC Motor Control HAT to make them move.
ConceptOctocat, GitHub’s mascot, is graphically depicted with five tentacles, which are the main characters of the show. Those have been crafted as soft robotics tentacles, as they closely mimic the natural movements of real tentacles. To achieve this, thick membranes with enclosed air channels have been constructed. These channels serve as pipes for air movement, enabling them to actuate the silicone tentacle in a lifelike manner. Notably, the position and shape of these air channels play the key role in determining the movement of the whole tentacle. For instance, we designed the tentacle to curl up both inwards and outwards. However, there are no limits to the design of the movement – get creative!
Controlling tentacles with pneumatic actuators can be an intriguing and innovative application, especially when combined with advanced control electronics. The Infineon TLE94112ES DC Motor Control HAT for Raspberry Pi offers 12 half-bridge drivers in one chip and can drive various actuators, including those used in pneumatic systems. The HAT complies with Raspberry Pi's HAT specification and comes with a C++ and Python library for Raspberry Pi.
TentaclesThe most interesting and also difficult part of the project are the tentacles. A mould and placeholders for the air pockets are required to produce the tentacles. Of the many known methods for producing soft robotics actuators, such as placeholders made of wax or other shrinking materials, or the production of two halves that are glued together, we decided to produce the air channels from water-soluble PVA filament. Once the silicone has hardened, it can be removed with water.
Now we can start by printing the mould, the placeholders for the air channels and a way of anchoring the two together. The mould and the anchoring can be printed with regular PLA filament and the air channel placeholders with water-soluble filament of your choice. To smooth out the texture of the tentacles, a spray-on filler can be used. In the pictures below you can see the mould and the water-soluble printed PVA filament.
Pour Silicone
The process of silicone casting is divided into two parts and requires casting tools such as standard measuring cups, stirring rods or spoons, a precision scale and, of course, silicone (preferably two-component silicone in Shore hardness A 10). The availability of a vacuum pump is essential, as otherwise the many air bubbles that are created during the process will cause the tentacle to burst when filled with air. The vacuum pump can be used to remove most of these quickly and easily.
The first step in the production of a tentacle is the moulding of the suction cups. These are blue-green in the Octocat. The placeholders for the air channels are not yet required for this step. First, apply separation spray to the mold.
You will need approx. 30 g of silicone (15 g per silicone component at a mixing ratio of 1:1) and a suitable silicone colour in green or blue in the specified mixing ratio.
After stirring briefly, the measuring cup can be placed in the vacuum pump and the pump switched on for 3-4 minutes. When the surface shows only a few bubbles, the silicone can be removed from the vacuum chamber.
The two mould halves can be tilted (see pictures) so that the silicone can spread evenly. In this step, you can also work imprecisely at first and pour the silicone onto the suction cups and then distribute it with your finger for a cleaner look. Now leave the silicone to cure for approx. 1-2 hours.
For the next step, you do not have to wait for the entire curing time, but only until the silicone stops flowing. Next, the placeholders for the air channels can also be sprayed with separation spray and then inserted into the mold. The mold can be secured with two clamps. Then position the mold so that the casting openings are facing upwards and click the anchoring firmly into place so that it sits firmly in the mold. A total of approx. 360 g silicone and black silicone color are required for the remaining tentacle. Mix these together and place in the vacuum chamber as in the previous step. When only a few bubbles are remaining, the measuring cup can be removed from the chamber. The silicone can now be poured through the openings until it is filled to the brim.
Once the silicone has cured (usually takes 2-3 hours), it can be removed from the mold. The next step is to carefully remove the front part of the anchors. To do this, the piece of PVA filament facing outwards must first be loosened with water. Finally, the tentacle must be trimmed. The thin edge can easily be removed with a cutter or a cuticle knife from the drugstore.
The Body is printed with black PLA-Filament and consists of the following parts:
- Octocat head and face (2 separate parts)
- A frame for the valves (many smaller parts that can be screwed together with M3x10 cylinder screws)
- A frame for the tentacles
- A base
- A carbon bar to place the tentacle frame on top
You can find all the CAD files attached in the end of this article. To give the head a more lively look, Octocat's face can be painted with acrylic paint.
Hardware Setup
The hardware setup consists of ten valves, some cables and a lot of pneumatic tubes. The valves need 400mA each, so up to 4A in total and then the Raspberry Pi still needs to be powered. Therefore, to be on the safe side, we decided to use a 12V/8A DC power supply. The HAT can be powered directly from this power supply and generates 5V/2.5A for back powering the Raspberry Pi. Now, let's take a look at the wiring of the valves.
Valves and Wiring
In the GIF above you can see how we have decided to wire all the valves. Each valve needs to be connected to GND (-) and to one half-bridge output of the HAT. So we go back to the power supply with one thick GND cable and connect ten individually controlled VCC (+12V) cables to the HAT.
Pneumatic
In this section we want to describe the pneumatic setup in more detail. Below you can see the printed frame, which contains the ten valves already. Each valve has connectors one is the input, so the connection to the air compressor. Then we have the output, which passes through the air from the input when the valve is supplied with voltage. And the third connector is the vent (golden part in the picture) which is used to get the air out of the system as soon as the valve is closed again. We used a choke in order to be able to control the airflow and to deflate the tentacles in the desired time. The blue part is a distributor, which distributes the input air into the fives valves of each side. This makes the overall system easier to connect and smaller.
We use push-fittings for the input and outputs. This makes the connecting of the compressed-air hose very simple and also makes it possible to easily disconnect the hose if needed.
In the second picture below you can see the tentacles already put into the 3d-printed part which holds all the five tentacles. They can be secured with screws to ensure that they are not falling out of the holder. What you can also see here are the two connections to the air courses of one tentacle. These two connecters have to be connected to the corresponding outputs of the vales.
Below, you can see the fitting that we made as connection to the compressor. It also uses a push-fitting to easily connect the hose. On the other end it has a fitting connector to the compressor. You can find a link to it in the "Things" section of this article.
Putting it together!
After we have prepared everything we can start to assemble everything together. As base plate we have decided to go with a wooden plate, which contains a 19 mm drilled holed in the center. Through this hole we will later put the power supply cable and the hose coming from the compressor. Above the hole we have designed a 3D-printed part which holds a carbon-enforced rod to hide the hose coming from the compressor as well as the cables going down to the Raspberry Pi HAT from the valves. The holder for the five tentacles sits on top of the carbon rod. The frame of the valves can be clipped on top of the tentacle holder.
As soon as everything is put together we can start to connect the missing hoses and the connections of the power supply as well as the connectors of valves. After all this is done you can test the final system if everything is leakproof. If that is the case and the tentacles are working, if you supply them with compressed air, the only thing left is clipping the head around the tentacle holder to hide the valves.
In order to bring the Octocat to life we use Infineon's multi-half-bridge library, available - guess where - on GitHub ;). This library includes examples for the TLE94112ES DC Motor Control HAT. In the Wiki of the repository you will also find instructions on how to install the library and how to get started with the library.
Protip: there is also a.... PROTIP available for the DC Motor control HAT we are using in this project. Ok, now let's come to the application code for the Octocat:
Let's start with the first sections of the code. Here, we include all necessary libraries, which are required to get the Raspberry Pi HAT running. After this we define the variable MOVEMENT_INTERVAL, which is the delay between the sequences of movements - we come to that point again later. The two overloaded log-functions simply help to debug the code and improve readability of the output.
/* Infineon library for multi half bridge */
#include "tle94112-rpi.hpp"
#include <bcm2835.h> // This library is required for the delay() function.
#include <cstdio>
#define MOVEMENT_INTERVAL 30000
void log(const char* message) {
printf("[...");
printf("]\t");
printf(message);
printf("\n");
}
void log(const char* unit, const char* message) {
printf("[");
printf(unit);
printf("]\t");
printf(message);
printf("\n");
}
The next part defines an Octocat class. I know... at first it looks a bit overwhelming, but actually the code is quite simple and this saves us a lot of redundancy within the code. So the first part is simply required to use the already existing Tle94112Rpi class of the multi-half-bridge library with the Octocat class. After that we define a cooldown time in milliseconds which is used between the single movements. We determined this time experimentally. It should allow to deflate the tentacles after each move, so they don't burst.
class Octocat {
public:
Tle94112Rpi *controller;
Octocat (Tle94112Rpi &driver) {
controller = &driver;
};
uint16_t cooldown = 300;
The following maps the single tentacle air valves to the half-bridge outputs of the TLE94112ES HAT. Don't worry this is not fixed. You can change this mapping to whatever fits you best. We will get to the meaning of "K" and "G" later.
Tle94112Rpi::HalfBridge VALVE_T1K = Tle94112Rpi::HalfBridge::TLE_HB3;
Tle94112Rpi::HalfBridge VALVE_T1G = Tle94112Rpi::HalfBridge::TLE_HB5;
Tle94112Rpi::HalfBridge VALVE_T2K = Tle94112Rpi::HalfBridge::TLE_HB12;
Tle94112Rpi::HalfBridge VALVE_T2G = Tle94112Rpi::HalfBridge::TLE_HB8;
Tle94112Rpi::HalfBridge VALVE_T3K = Tle94112Rpi::HalfBridge::TLE_HB2;
Tle94112Rpi::HalfBridge VALVE_T3G = Tle94112Rpi::HalfBridge::TLE_HB1;
Tle94112Rpi::HalfBridge VALVE_T4K = Tle94112Rpi::HalfBridge::TLE_HB11;
Tle94112Rpi::HalfBridge VALVE_T4G = Tle94112Rpi::HalfBridge::TLE_HB7;
Tle94112Rpi::HalfBridge VALVE_T5K = Tle94112Rpi::HalfBridge::TLE_HB10;
Tle94112Rpi::HalfBridge VALVE_T5G = Tle94112Rpi::HalfBridge::TLE_HB9;
The next enumeration defines the single tentacles and groups of it. This enum is used later to easily address the single tentacles or a group of them.
enum Tentacle
{
TENTACLE_1 = 0, TENTACLE_2, TENTACLE_3, TENTACLE_4, TENTACLE_5,
TENTACLE_FRONT, TENTACLE_DIAG_RL, TENTACLE_RIGHT, TENTACLE_LEFT, TENTACLE_DIAG_LR, TENTACLE_BACK,
TENTACLE_FRONT_BACK, TENTACLES_ALL
};
Generally, we have two different movements. This first on is lifting the tentacle, which happens when you inflate the larger air cell ("G"). The second movement is crouching the tentacle by inflating the small air chamber ("K").
enum TentacleMove
{
TENTACLE_LIFT = 0, TENTACLE_CROUCH
};
The last enumeration is used to define delays for the single tentacles as well for groups of them. The timing here is crucial, because too short might not move the tentacles and look boring, while too long times might destroy them. There are individual inflation times dependent on the number of tentacles you control in parallel and of course depending on the air chamber size. Usually the larger air chamber needs a bit more on-time and when you decide to switch multiple tentacles at once, you will also need a longer on-time, because the airflow distributes amongst the tentacles. We determined these values also in careful experiments.
enum TentacleDelays
{
/*
CAREFULLY! tune these parameters - start with small values.
The longer the delay, the higher the posibility to damage a tentacle!
CROUCH refers to the small air chamber, LIFT to the big one.
The number refers to the number of switched tentacles.
*/
TENTACLE_DELAY_CROUCH_1 = 50, TENTACLE_DELAY_LIFT_1 = 350,
TENTACLE_DELAY_CROUCH_2 = 80, TENTACLE_DELAY_LIFT_2 = 350,
TENTACLE_DELAY_CROUCH_3 = 100, TENTACLE_DELAY_LIFT_3 = 400,
TENTACLE_DELAY_CROUCH_ALL = 200, TENTACLE_DELAY_LIFT_ALL = 450
};
The following functions are all used to control the tentacles or groups of them. Here, we want to highlight the moveTentacle function, which will be the exposed part of the class later and the function used to actually control the tentacles from the code. It takes two parameters, the tentacle or the group of tentacles that you want to control and which movement they should perform, so lift or crouch. The correct usage of the function can be seen in the main-loop of the code in the next section.
void operateHalfBridge(Tle94112Rpi::HalfBridge hb, Tle94112Rpi::HBState state) {
char message[100];
sprintf(message, "HB:%d\tSTATE:%d", (int)hb, (int)state);
log("OPERATE", message);
controller->configHB(hb, state, Tle94112Rpi::TLE_NOPWM);
}
void operateValve(Tle94112Rpi::HalfBridge valve, uint16_t openTime) {
operateHalfBridge(valve, Tle94112Rpi::TLE_HIGH);
delay(openTime);
operateHalfBridge(valve, Tle94112Rpi::TLE_FLOATING);
};
void operateValve(Tle94112Rpi::HalfBridge valve1, Tle94112Rpi::HalfBridge valve2, uint16_t openTime) {
operateHalfBridge(valve1, Tle94112Rpi::TLE_HIGH);
operateHalfBridge(valve2, Tle94112Rpi::TLE_HIGH);
delay(openTime);
operateHalfBridge(valve1, Tle94112Rpi::TLE_FLOATING);
operateHalfBridge(valve2, Tle94112Rpi::TLE_FLOATING);
};
void operateValve(Tle94112Rpi::HalfBridge valve1, Tle94112Rpi::HalfBridge valve2, Tle94112Rpi::HalfBridge valve3, uint16_t openTime) {
operateHalfBridge(valve1, Tle94112Rpi::TLE_HIGH);
operateHalfBridge(valve2, Tle94112Rpi::TLE_HIGH);
operateHalfBridge(valve3, Tle94112Rpi::TLE_HIGH);
delay(openTime);
operateHalfBridge(valve1, Tle94112Rpi::TLE_FLOATING);
operateHalfBridge(valve2, Tle94112Rpi::TLE_FLOATING);
operateHalfBridge(valve3, Tle94112Rpi::TLE_FLOATING);
};
void moveAll(TentacleMove move) {
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T1K : VALVE_T1G, Tle94112Rpi::TLE_HIGH);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T2K : VALVE_T2G, Tle94112Rpi::TLE_HIGH);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T3K : VALVE_T3G, Tle94112Rpi::TLE_HIGH);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T4K : VALVE_T4G, Tle94112Rpi::TLE_HIGH);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T5K : VALVE_T5G, Tle94112Rpi::TLE_HIGH);
delay(move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_ALL : TENTACLE_DELAY_LIFT_ALL);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T1K : VALVE_T1G, Tle94112Rpi::TLE_FLOATING);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T2K : VALVE_T2G, Tle94112Rpi::TLE_FLOATING);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T3K : VALVE_T3G, Tle94112Rpi::TLE_FLOATING);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T4K : VALVE_T4G, Tle94112Rpi::TLE_FLOATING);
operateHalfBridge(move == TENTACLE_CROUCH ? VALVE_T5K : VALVE_T5G, Tle94112Rpi::TLE_FLOATING);
};
void stopAll() {
log("HB STOP", "ALL HBs to FLOATING");
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB1, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB2, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB3, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB4, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB5, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB6, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB7, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB8, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB9, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB10, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB11, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
controller->configHB(Tle94112Rpi::HalfBridge::TLE_HB12, Tle94112Rpi::TLE_FLOATING, Tle94112Rpi::TLE_NOPWM);
};
void moveTentacle(Tentacle tentacle, TentacleMove move) {
switch(tentacle) {
case (TENTACLE_1):
operateValve(move == TENTACLE_CROUCH ? VALVE_T1K : VALVE_T1G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_1 : TENTACLE_DELAY_LIFT_1);
break;
case (TENTACLE_2):
operateValve(move == TENTACLE_CROUCH ? VALVE_T2K : VALVE_T2G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_1 : TENTACLE_DELAY_LIFT_1);
break;
case (TENTACLE_3):
operateValve(move == TENTACLE_CROUCH ? VALVE_T3K : VALVE_T3G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_1 : TENTACLE_DELAY_LIFT_1);
break;
case (TENTACLE_4):
operateValve(move == TENTACLE_CROUCH ? VALVE_T4K : VALVE_T4G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_1 : TENTACLE_DELAY_LIFT_1);
break;
case (TENTACLE_5):
operateValve(move == TENTACLE_CROUCH ? VALVE_T5K : VALVE_T5G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_1 : TENTACLE_DELAY_LIFT_1);
break;
case (TENTACLE_FRONT):
operateValve(move == TENTACLE_CROUCH ? VALVE_T1K : VALVE_T1G, move == TENTACLE_CROUCH ? VALVE_T2K : VALVE_T2G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_2 : TENTACLE_DELAY_LIFT_2);
break;
case (TENTACLE_DIAG_RL):
operateValve(move == TENTACLE_CROUCH ? VALVE_T1K : VALVE_T1G, move == TENTACLE_CROUCH ? VALVE_T3K : VALVE_T3G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_2 : TENTACLE_DELAY_LIFT_2);
break;
case (TENTACLE_RIGHT):
operateValve(move == TENTACLE_CROUCH ? VALVE_T1K : VALVE_T1G, move == TENTACLE_CROUCH ? VALVE_T5K : VALVE_T5G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_2 : TENTACLE_DELAY_LIFT_2);
break;
case (TENTACLE_LEFT):
operateValve(move == TENTACLE_CROUCH ? VALVE_T2K : VALVE_T2G, move == TENTACLE_CROUCH ? VALVE_T3K : VALVE_T3G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_2 : TENTACLE_DELAY_LIFT_2);
break;
case (TENTACLE_DIAG_LR):
operateValve(move == TENTACLE_CROUCH ? VALVE_T2K : VALVE_T2G, move == TENTACLE_CROUCH ? VALVE_T5K : VALVE_T5G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_2 : TENTACLE_DELAY_LIFT_2);
break;
case (TENTACLE_BACK):
operateValve(move == TENTACLE_CROUCH ? VALVE_T3K : VALVE_T3G, move == TENTACLE_CROUCH ? VALVE_T5K : VALVE_T5G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_2 : TENTACLE_DELAY_LIFT_2);
break;
case (TENTACLE_FRONT_BACK):
operateValve(move == TENTACLE_CROUCH ? VALVE_T1K : VALVE_T1G, move == TENTACLE_CROUCH ? VALVE_T2K : VALVE_T2G, move == TENTACLE_CROUCH ? VALVE_T4K : VALVE_T4G, move == TENTACLE_CROUCH ? TENTACLE_DELAY_CROUCH_3 : TENTACLE_DELAY_LIFT_3);
break;
case (TENTACLES_ALL):
moveAll(move);
break;
}
log("COOLDOWN", "WAITING");
delay(cooldown);
};
};
Main-Loop
Now we will take a brief look into the main-loop, First of all we have to define and initialize a Tle94112Rpi object, in order to pass it to our constructor of the newly created Octocat class.
int main(int argc, char const *argv[])
{
log("MAIN", "Initializing Half Bridge Controller...");
Tle94112Rpi controller;
controller.begin();
// Clear all errors to start clean
controller.clearErrors();
// Initialize Octocat
log("MAIN", "Octocat start...");
Octocat oc(controller);
After that we define the sequence of movements the tentacles should perform. Here, you can see the correct usage of the moveTentacle function as mentioned above. In our case we defined a very simple choreography which controls the tentacles in a certain order that we found appealing and funny in our tests. After that we close all the valves with the stopAll function (this is redundant, but after putting so much effort in building the tentacles we want to make sure they don't burst) and wait for MOVEMENT_INTERVAL milliseconds before we start the same choreography again. You can of course change the sequence in any way you like, feel also free to add more groups of tentacles in the enum before or just randomly inflate the tentacles. Here, you have all the freedom you want and this code should only serve as starting point.
// Octocat loop
while (1) {
// Front/back movement
for (uint8_t i = 0; i < 3; i++) {
oc.moveTentacle(oc.TENTACLE_FRONT_BACK, oc.TENTACLE_CROUCH);
oc.moveTentacle(oc.TENTACLE_FRONT_BACK, oc.TENTACLE_LIFT);
oc.moveTentacle(oc.TENTACLE_BACK, oc.TENTACLE_LIFT);
oc.moveTentacle(oc.TENTACLE_BACK, oc.TENTACLE_CROUCH);
}
// Round movement
for (uint8_t i = 0; i < 2; i++) {
oc.moveTentacle(oc.TENTACLE_1, oc.TENTACLE_CROUCH);
oc.moveTentacle(oc.TENTACLE_1, oc.TENTACLE_LIFT);
oc.moveTentacle(oc.TENTACLE_2, oc.TENTACLE_CROUCH);
oc.moveTentacle(oc.TENTACLE_3, oc.TENTACLE_LIFT);
oc.moveTentacle(oc.TENTACLE_4, oc.TENTACLE_CROUCH);
oc.moveTentacle(oc.TENTACLE_4, oc.TENTACLE_LIFT);
oc.moveTentacle(oc.TENTACLE_5, oc.TENTACLE_CROUCH);
oc.moveTentacle(oc.TENTACLE_5, oc.TENTACLE_LIFT);
}
// Side movement
for (uint8_t i = 0; i < 2; i++) {
oc.moveTentacle(oc.TENTACLE_LEFT, oc.TENTACLE_CROUCH);
oc.moveTentacle(oc.TENTACLE_LEFT, oc.TENTACLE_LIFT);
oc.moveTentacle(oc.TENTACLE_RIGHT, oc.TENTACLE_LIFT);
oc.moveTentacle(oc.TENTACLE_RIGHT, oc.TENTACLE_CROUCH);
}
// Make sure to have all valves closed at end
oc.stopAll();
log("MAIN", "Waiting for movement interval...");
delay(MOVEMENT_INTERVAL);
}
// Make sure to have all valves closed at end
oc.stopAll();
}
Finally, you can see the Octocat moving:
Comments