# Winkekatzewinkenlassenanleitung
![](https://hackmd.unserver.de/uploads/4a55394f-0678-429f-bd52-674168ad6318.png =500x) ![](https://hackmd.unserver.de/uploads/d1ce2501-2636-4fb2-a0a6-d6701751b1af.png =200x)
Das hier ist eine ausführliche Anleitung für Leute die nicht viel Erfahrung mit Microcontrollern haben und gerne verstehen wollen was sie tun. Um ein bischen löten kommt man leider nicht herum. Wer null Erfahrung dabei hat sollte sich eine Anleitung zum Löten im Netz suchen. Um diese ganze Anleitung hier durchzugehen und am Ende eine fertige Katze zu haben, sollte man sich einen ganzen Tag, oder so 3 Abende Zeit nehmen. Wer einfach schnellstmöglichst zum Ergebnis kommen will kann sich natürlich auch einfach den [Schaltplan](#Steckkatze-aufbauen) unten anschauen und direkt aufs Board löten.
Alle müssen sich entscheiden wie der Winkearm der Katze angesteuert wird.
:::info
Die klassische Methode ist mit einem Servomotor der an dem Arm befestigt wird. Die Schwingung ist allerdings etwas laut. Abschnitte für den **Servo** Motor markiere ich blau.
:::
:::success
Abschnitte die nur für die **Magnetschwingung** wichtig sind markiere ich grün. Die Lösung die eingebaute Magnetschwingung anzutreiben um den Arm schwingen zu lassen ist deutlich leiser, allerdings nur für sehr kleine Winkkatzen (9 cm hoch) getestet. Funktioniert natürlich nur wenn man eine funktionierende Winkekatze als 'Rohmaterial' hat.
:::
## Was es braucht
- Winkekatze oder etwas Winkekatzenähnliches (Klopapierrolle?)
- Werkzeug um die Winkekatze zu öffnen und Augen zu bohren (Hammer+Nagel, Schraubenzieher,..)
- Board (D1 mini, z.b hier: https://www.makershop.de/plattformen/d1-mini/esp8266-mini-board/)
- Daten! Micro USB kabel. Die billigen Handyladekabel geben oft nur Strom ohne Datenübertragung
- 2 programmierbare RGB LEDSs
https://www.berrybase.de/bauelemente/aktive-bauelemente/leds/digitale-leds/adressierbare-ws2812-rgb-led-pth-5mm-diffus-5er-pack
die sind etwas schwieriger zu bekommen. sonst z.b hier: https://www.ebay.de/itm/173282382294?hash=item28586f51d6:g:FfEAAOSw8Sta3HBl oder auf ebay schauen nach APA106 F5 oder P9823 oder ws2812B oder YF923. Wichtig ist, das die 4 pins haben, einen Ground, einen Vin, einen daten out und einen Daten in
:::success
- für die Magnetschwingung eine npn Diode -z.b bc547
https://www.conrad.de/de/p/diotec-transistor-bjt-diskret-bc547c-to-92-3-anzahl-kanaele-1-npn-140539.html
:::
:::info
- Für die Servo-methode 1 Servo, z.B von hier (https://www.makershop.de/sonstiges/tower-pro-micro-servo/) oder hier https://www.conrad.de/de/p/makerfactory-mf-6402123-motor-1-st-2134041.html
:::
- Widerstände ca. 100 Ohm und 1k Ohm
- Breadboard z.B https://www.makershop.de/zubehoer/breadboard/mini-breadboard-170/ oder https://www.makershop.de/zubehoer/breadboard/mb-120-830-breadboard/
- männlich,männlich Stecker https://www.makershop.de/zubehoer/kabel/jumper-wire-kabel-20p/
- sehr praktisch ist ein Multimeter mit Piepser, oder eine einfache TestLED mit zwei Beinchen
https://www.conrad.de/de/p/tru-components-1577379-led-bedrahtet-rot-rund-3-mm-125-mcd-60-20-ma-1577379.html
- (nicht unbedingt notwendig) Lüsterklemmen
- Lötzeug (inklusive ein bischen Draht)
- Laptop oder Rechner
![](https://hackmd.unserver.de/uploads/5624c65a-5f3a-44c1-a964-918ffe5f8ea5.jpg)
Ein Bild mit 'allen' Dingen die man brauchen kann
## Software installieren
- Das hier istallieren (vielleicht geht das auch mit der Web- version?- nicht getestet) https://www.arduino.cc/en/software
Eventuell/ sollte er beim installieren einfordern: Add yourself to the uucp group to access the serial ports:
sudo usermod -a -G uucp <user>
- Einrichten das das mit dem D1 mini funktioniert, wird z.B hier erklärt https://www.iotstarters.com/coding-the-wemos-d1-mini-using-arduino-ide/
- Schauen ob das Blinke-Testprogram unter dem Link oben funktioniert
- Alle benötigten libs installieren: unter Sketch -> include Library -> Manage Libraries die Folgenden suchen und installieren. Die sind alphabetisch geordnet, nach dem suchen muss man etwas runterscrollen.
- 'Adafruit NeoPixel' (ohne DMA) für die LED Ansteuerung
- 'PubSubClient' (ohne MQTT)
- WiFiManager
- ArduinoJson
## Board löten
Weil wir die Komponenten erst mal mit einer Steckverbindung testen wollen müssen wir die Steckverbindung auflöten. Lange oder kurze Beinchen sind egal. Auf welche Seite des Chips man das macht auch. Wer die Steckverbindung am Ende für die fertige Katze wieder ablöten will macht es sich einfacher, wenn er nur die Pins: G , D1 , D2 , und 3V3 festlötet.
| ![](https://hackmd.unserver.de/uploads/f903aa49-0bd5-46f5-8819-261549b38c68.jpg =300x300) |![](https://hackmd.unserver.de/uploads/04c59467-eed1-453f-8f04-7f6a49f160bd.jpg =300x300)|
| -------- | -------- |
Steckverbindung aufgelötet.
## Augen testen
- Schaut wo welche Anschlüsse bei euren LEDs sind (Datenblatt). In dem Beispiel unten ist es von der flachen zur runden Seite: Daten raus (Dout), GND (-), VDD (+), Daten rein (Din)
- Schaut wie die Verbindungen eures Steckboards sind (längs/quer)
- Beide GND Anschlüsse eurer LEDS müssen an den Ground des W1 minis angeschlossen werden, beide VDD an die 3,3 V. Benutzt dafür die Kabel und das Steckboard. Din der einen LED muss an einen Datenpin des Boards, Dout an Din der zweiten LED. Dout der zweiten LED kann leer bleiben. An welchen Datenpin die erste LED angeschlossen wird ist egal. Er muss dann nur im Code unten entsprechend eingetragen werden.
- Die Pinbelegung des W1 mini findet man z.B hier: https://www.iotstarters.com/coding-the-wemos-d1-mini-using-arduino-ide/
Beispiel: der Pin 4 ist GPIO4, also bei D2 !
- Kopiere den Blinketest code unten, schaue ob die Pinbelegungen passen, lade das Program auf dem W1 und schaue ob es richtig blinkt.
- Wenn nicht, schaue mit der Test LED ob Strom ankommt, schaue ob alle Verbindungen richtig sind, die Pinbelegung stimmt, die Beinchen der LED richtig zugeordnet wurden...
----
```c++=
// Blinkentest Code
#include <Adafruit_NeoPixel.h>
const int LEDPin = 5; //#d1
const int numLEDs = 2;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(numLEDs, LEDPin, NEO_RGB + NEO_KHZ800);
auto RED = pixels.Color(255,0,0);
auto GREEN = pixels.Color(0, 255,0);
auto BLUE = pixels.Color(0, 0, 255);
void setup() {
Serial.begin(115200);
Serial.println('test');
pixels.begin();
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println('red');
for ( int i = 0; i < numLEDs; i++) {
pixels.setPixelColor(i, RED);
}
pixels.show();
delay(1000); // Wait for 1 second
Serial.println('blue');
for ( int i = 0; i < numLEDs; i++) {
pixels.setPixelColor(i, BLUE);
}
pixels.show();
delay(1000);
Serial.println("green");
for ( int i = 0; i < numLEDs; i++) {
pixels.setPixelColor(i, GREEN);
}
pixels.show();
delay(1000);
}
```
## Steckkatze aufbauen
Hier wird jetzt die ganze Katze aufgebaut. Gute Nachricht: Die LEDs können erst mal genau so bleiben! Was dazukommt ist die Winkevorrichtung.
Dafür schraub die Katze auf (verlier die Schrauben nicht..).
:::success
Für Magnetlösung:
Entweder ein Seckkabel durchschneiden (schöner ist ein rotes und ein Schwarzes) und den angeschnittenen Teil anlöten, oder ein normales Kabel löten und dann mit Lüsterklemmen mit einem Steckkabel verbinden.
Löte an den + Pol des Batteriefachs (das ohne die Spirale) ein rotes Kabel, an den - Pol ein schwarzes. Weiter unten ist ein Schaltplan und ein Foto zur Veranschaulichung. Den Rest aufbauen wie auf dem Schaltplan und dem Foto ersichtlich.
:::
:::info
Für Servolösung: Die Achse vom Servo irgendwie mit der Achse des Armes verbinden. Heißkleber? Bei der Klopapierrollenkatze passt ein Schaschlikspieß wunderbar als Arm in die Achse. Unten sieht man ein Bild von dem aufbau für die Magnetlösung.
Für die Servolösung wird anstelle von Widerstand und Transistor direkt der Daten Pin mit dem Daten-in des Servos verbunden. VCC und GROUND kommen an + und - .
:::
![](https://hackmd.unserver.de/uploads/ec3acb26-b213-4f06-b0c8-ac19c8d52a95.png)
Schaltplan für die Magnetlösung
| Bild für die Magnetlösung| Bild von gelöteter Winkekatze |
| -------- | -------- |
| ![](https://hackmd.unserver.de/uploads/524ba2c0-1009-44ac-ba51-54f8de0e7f1c.jpg) | ![](https://hackmd.unserver.de/uploads/8c70b907-96fc-44e5-991e-d7d3824881ae.jpg) |
## Winkecode
Program (gaaanz [unten](#Program-fuer-Magneta)) in die Arduino IDE kopieren. Es gibt auch ein gitlab, je nachdem wie alt die Anleitung hier ist will man da mal reinschauen ob es dort neueren Code gibt: https://git.weitnahbei.de/buero/jimmykater
Stand 2022 ist dort aber z.B die Magnetlösung noch nicht implementiert.
Die Winkeoption ist unter 'void wink()' definiert, momentan z.249.
:::success
Wer die Magnetlösung wählt und sich erst mal nicht mit dem Code befassen will, kann den einfach auf den W1 laden.
In der Winkeoption-Funktion werden die Farben der Augen gesetzt, der Transistor so gesetzt das das Winken zugelassen wird, und dann beides wieder auf inaktiv gesetzt. Wer das Winken verkürzen/verlängern will setzt den delay Wert - delay(2000) - größer oder kleiner.
:::
:::info
Für die Lösung mit einem Servo Motor muss der Code wie unten, unter (Winkecode für Servo) angegeben abgewandelt werden.
#### Winkecode für Servo
Wer **stattdessen** einen Servo einbauen will lässt in der wink() Funktion die Aufrufe mit transistorPin und den Delay dazwischen weg und fügt an die Stelle (z.B) folgendes ein (das ahmt eine Sinuusschwingung nach):
```c++=258
myservo.attach(servoPin);
delay(20);
myservo.write(midPosition);
delay(300);
for( float t = 0.0; t < 2000; t += 15 ) {
float pos = midPosition + 70.0*sin( w * t ) * pow(2.714, -(w/15.0) * t);
myservo.write((int) pos);
delay(15);
};
myservo.write(midPosition);
delay(20);
myservo.detach();
```
kommentiert
```
#include <Servo.h>
```
ein,
nimmt alles mit 'transistorPin' raus
und ersetzt
```c++=24
const int transistorPin = 4;// #d2
```
mit
```c++=24
Servo myservo;
const int servoPin = 4;
const int midPosition = 100;
const float pi = 3.14;
const float w = 2*pi/800;
```
Wer das Winken verkürzen/verlängern will kann die for schlaufe für das t verkürzen/verlängern.
:::
Die Katze sollte nach dem upload ein eigenes W-lan 'Winkekatze24' erstellen. Damit verbinden, Winkekatze benennen und Zugang zum lokalem W-lan geben.
Hier sollte die Winkekatze jetzt unter dem gegebenen Namen auftauchen, und winken und leuchten wenn man den entsprechenden Knopf drückt.
https://mad.unserver.de/winketest/
## Hardware Bastelei
Loch in Augen machen, z.B vorsichtig mit Akkubohrer oder einem Nagel (Katze so umdrehen, dass sie dabei auf die Tischplatte schaut! Nicht so wie im Bild links unten, sonst splittert sie!). Ich mochte die Methode das mit einem Hand-Schraubenzieher zu machen.
![](https://hackmd.unserver.de/uploads/42520390-f368-42a4-902f-d46e81d5fec0.jpeg =300x)| ![](https://hackmd.unserver.de/uploads/f3cb0d53-0d51-478d-9f51-9a7b351a6bd5.jpeg =300x)
Überlege dir wie das Board nachher in der Katze untergebracht sein soll, eventuell braucht man ein Loch im Hintern für den USB Stecker. Bei meiner Lösung steht die Katze einfach auf einem Podest, der Chip ist untendrin.
![](https://hackmd.unserver.de/uploads/22424874-d0d7-4071-8247-540b8768479d.jpg =300x) |![](https://hackmd.unserver.de/uploads/b78c5088-a55d-4191-89f2-a73bfcc86a6b.jpg =300x)
## Löten
Dann 'einfach' alles was vorher gesteckt war schön ordentlich löten.
LEDs mit Kabel verlöten, LEDs mit Tape in die gelöcherten Augen kleben. Achte dabei darauf, dass das Tape den Winkearm nicht blockiert.
Verlöte die restlichen Teile. Je nach Platzbedarf musst du die Steckvorrichtung dafür wieder rauslöten, oder nicht. Die Bilder unten zeigen Beispiele wie man das löten kann. Oben links ein Beispiel in dem die Steckverbindungen bei den LEDs verbleiben, rechts mit direkter Verlötung. Überlegt euch aber am besten Selber wie ihr den ganzen Kram am schönsten Verbindet :)
| Column 1 | Column 2 |
| -------- | -------- |
| ![](https://hackmd.unserver.de/uploads/719903a4-e425-49d1-b6d9-0d9be2bcec93.png =300x) mögliche Kabelführung | ![](https://hackmd.unserver.de/uploads/1240652f-b8bb-4821-942e-d14d3ba824a0.jpg) Verbindung der LEDs |
| ![](https://hackmd.unserver.de/uploads/5464a775-ce14-4844-b394-0ca7c8bf96c7.jpg) gelötete Verbindungen auf der Platine | ![](https://hackmd.unserver.de/uploads/433c7cb6-a4c7-4470-a8ef-f49d8d77f45e.jpg) nochmal von der Seite |
## Fast Geschafft!
Schrauben wieder finden und Katze zuschrauben. Bei der Winketest Seite kannst du deine eigene Katze testen, oder unter https://www.winkekatze24.de/ alle angeschlossenen Winkekatzen gleichzeitig winken lassen.
:heart_eyes_cat:
## Program fuer Magneta
Stand 2022
```c++=
#include <FS.h> // SPIFFS Filesystem
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
#include <PubSubClient.h>
//#include <Servo.h>
#include <stdlib.h>
#include <Adafruit_NeoPixel.h>
// WifiManager
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <ArduinoJson.h>
//https://github.com/bblanchon/ArduinoJson
// this can stay test.mosquitto.org for the global clowder
char mqtt_server[255] = "mqtt.winkekatze24.de";
char mqtt_port[6] = "1883";
char cat_name[34] = "Magneta24";
const int transistorPin = 4;// #d2
// LEDs connected to GPIO0. They are running with a lower voltage(around 4.3V) so the 3.3V output level is enough to trigger high const
int LEDPin = 5; //#d1
const int numLEDs = 2;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(numLEDs, LEDPin, NEO_RGB +
NEO_KHZ800);
auto RED = pixels.Color(255,0,0);
auto GREEN = pixels.Color(0, 255,0);
auto BLUE = pixels.Color(0, 0, 255);
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
uint32_t eyecolor = 0;
void setup_wifi();
void callback(char* topic, byte* payload, unsigned int length);
void wink();
void eye_debug(uint32_t color) {
for ( int i = 0; i < numLEDs; i++ ) {
pixels.setPixelColor(i, color);
}
pixels.show();
};
//flag for saving data
bool shouldSaveConfig = false;
void configModeCallback(WiFiManager *myWiFiManager) {
eye_debug(pixels.Color(255,0,255));
}
//callback notifying us of the need to save config
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}
void readConfig() {
//clean FS, for testing
//SPIFFS.format();
if (SPIFFS.begin()) {
Serial.println("mounted file system");
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
Serial.println("reading config file");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonDocument doc(1024);
deserializeJson(doc, buf.get());
serializeJson(doc, Serial);
if (!doc.isNull()) {
Serial.println("\nparsed json");
strcpy(mqtt_server, doc["mqtt_server"]);
strcpy(mqtt_port, doc["mqtt_port"]);
strcpy(cat_name, doc["cat_name"]);
} else {
Serial.println("failed to load json config");
}
}
}
} else {
Serial.println("failed to mount FS");
}
//end read
}
void setup() {
// if we didn't use the serial output we could gain 2 GPIOs.
Serial.begin(115200);
Serial.write("jimmkater booting....");
pinMode(transistorPin, OUTPUT);
Serial.write("output");
digitalWrite(transistorPin,LOW);
Serial.write("low");
eyecolor = pixels.Color(255, 0, 255);
pixels.begin();
eye_debug(GREEN);
readConfig();
Serial.write("Config was interesting");
setup_wifi();
Serial.write("wifi read");
client.setServer(mqtt_server, 1883);
Serial.write("set Server");
client.setCallback(callback);
Serial.write("callback");
eye_debug(pixels.Color(0,255,255));
delay(50);
eye_debug(pixels.Color(0,0,0));
}
void setup_wifi() {
// The extra parameters to be configured (can be either global or just in the setup) // After connecting, parameter.getValue() will get you the configured value // id/name placeholder/prompt default length
WiFiManagerParameter custom_mqtt_server("server", "mqtt server",mqtt_server, 40); WiFiManagerParameter custom_mqtt_port("port", "mqttport", mqtt_port, 5); WiFiManagerParameter custom_cat_name("cat_name","the cat's name", cat_name, 32);
WiFiManager wifiManager;
eye_debug(BLUE);
//set config save notify callback
wifiManager.setSaveConfigCallback(saveConfigCallback);
wifiManager.setAPCallback(configModeCallback);
//add all your parameters here
wifiManager.addParameter(&custom_mqtt_server);
wifiManager.addParameter(&custom_mqtt_port);
wifiManager.addParameter(&custom_cat_name);
wifiManager.setConfigPortalTimeout(120);
if (!wifiManager.autoConnect("Winkekatze24")) {
eye_debug(RED);
Serial.println("failed to connect and hit timeout");
delay(3000);
eye_debug(BLUE);
delay(3000);
eye_debug(RED);
delay(3000);
eye_debug(BLUE);
//reset and try again, or maybe put it to deep sleep
ESP.reset();
delay(500);
}
//read updated parameters
strcpy(mqtt_server, custom_mqtt_server.getValue());
strcpy(mqtt_port, custom_mqtt_port.getValue());
strcpy(cat_name, custom_cat_name.getValue());
//save the custom parameters to FS
if (shouldSaveConfig) {
Serial.println("saving config");
DynamicJsonDocument json(1024);
json["mqtt_server"] = mqtt_server;
json["mqtt_port"] = mqtt_port;
json["cat_name"] = cat_name;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("failed to open config file for writing");
}
serializeJson(json, Serial);
serializeJson(json, configFile);
configFile.close();
//end save
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.println(String(topic));
if ( String(topic) == String(cat_name) + "/paw/command" ) {
char* p = (char*)malloc(length + 1);
p[length] = 0;
// Copy the payload to the new buffer
memcpy(p, payload, length);
int ircode = atoi(p);
digitalWrite(transistorPin,HIGH);
delay(2000); // wait 2 sec.
digitalWrite(transistorPin,LOW);
free(p);
} else if ( String(topic) == String(cat_name) + "/command" ||
String(topic) == "winkekatze/allcats" ) { wink();
} else if ( String(topic) == "fux/door/status" ) {
wink();
} else if ( String(topic) == "winkekatze/allcats/eye/set" ) {
char* p = (char*)malloc(length + 1);
p[length] = 0;
// Copy the payload to the new buffer
memcpy(p, payload, length);
String colstr(p);
Serial.println(colstr);
if ( colstr == "pink" ) {
eyecolor = pixels.Color(255, 0, 255);
} else if ( colstr == "red" ) {
eyecolor = pixels.Color(255, 0, 0);
} else if ( colstr == "green" ) {
eyecolor = pixels.Color(0, 255, 0);
} else if ( colstr == "blue" ) {
eyecolor = pixels.Color(0, 0, 255);
} else if ( colstr == "cyan" ) {
eyecolor = pixels.Color(0, 255, 255);
} else if ( colstr == "yellow" ) {
eyecolor = pixels.Color(255, 255, 0);
}
free(p);
}
client.publish((String(cat_name)+"/status").c_str(), "fishing");
}
void wink() {
for ( int i = 0; i < numLEDs; i++) {
pixels.setPixelColor(i, eyecolor);
}
pixels.show();
Serial.write("eyes should be set..");
digitalWrite(transistorPin,HIGH);
delay(2000); // wait 2 sec.
digitalWrite(transistorPin,LOW);
for ( int i = 0; i < numLEDs; i++) {
pixels.setPixelColor(i, pixels.Color(0, 0 ,0));
}
pixels.show();
delay(1000); // wait 1 sec. to protect from overwinking
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(cat_name, (String(cat_name) +
"/connected").c_str(), 2, true, "0")) { Serial.println("connected");
// Once connected, publish an announcement...
client.publish((String(cat_name) + "/connected").c_str(), "1",
true); // ... and resubscribe
client.subscribe( ( String(cat_name) + "/paw/command").c_str() );
client.subscribe( (String(cat_name) + "/command" ).c_str() );
client.subscribe("winkekatze/allcats");
client.subscribe("winkekatze/allcats/eye/set");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 2 seconds");
// Wait 2 seconds before retrying
delay(2000);
}
}
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}
```