Senso – selbst entwickelt

Zum Senso: Gehäuse im 3D-Druck erzeugt und aus lasergeschnittenen Arcylteilen gefertigt. Hardware: Arduino Standard, Mp3-Player (DFPlayer mini), LCD-Display 16×2, 4 Tastenfelder jeweils mit individuell ansteuerbarer LED und 4 zusätzliche Bedientasten. Funktionen: Verschiedene Spiele mit Licht- und Soundeffekten und MP3-Player zum Musikabspielen mit verschiedenen Lichteffekten. Besonderheiten: Objektorientierte Software, die speziell mit den beschränkten Fähigkeiten eines normalen Arduino umgehen muss in Bezug auf Speicherkapazität und Rechenleistung. Ergebnis: Absolut direkte Bedienung ohne jegliche Hänger. Reagiert jederzeit auf alle Tastendrücke. Ebenfalls Aufbau einer Menüstruktur, die mit den geringen Ressourcen umgehen kann und eine gute Basis ist für weitere Projekte. Mit dieser Software wurde speziell gezeigt, wie eine Echtzeitfähigkeit mit einem Mikrocontroller erreicht werden kann.

 

 

Elch zu Weihnachten

Der Elch als Weihnachtsschmuck ist ein kleines Projekt, welches mit einem Arduino Uno oder Klone, einer LED mit Vorwiderstand und einem kleinen Servo auskommt. Die Besonderheit liegt darin, dass der Elch mit dem USB-Kabel, mit dem er über die Arduino IDE programmiert wird, mit dem PC verbunden bleibt. Dann wird das Python3-Skript Elch_1.py gestartet, welches im aktuellen Verzeichnis die Datei Elch.png benötigt. Zwischen dem PC und dem Arduino wird nun eine serielle Verbindung aufgebaut, die als Beispiel für eine fehlertolerante und besonders zuverlässige serielle Datenübertragung herangezogen werden kann.

Jetzt kann man wunderschön ein Setup für den Elch finden, welches einem besonders gefällt: Man schaltet die Automatik aus, fährt den Kopf in die gewünschte obere Position und speichert diese ab und macht dasselbe mit der unteren Position. Anschließend werden noch die Nickfrequenz des Kopfes und die Blinkfrequenz der LED eingestellt. Wer will kann auch im Stillstand einfach nur die Helligkeit der LED regeln. Wenn alles gespeichert ist, dann läuft der Elch auch ohne das Python Skript mit jeder USB-Stromversorgung oder einem Arduino Uno geeigneten Netzteil.

Damit eine serielle Verbindung zustande kommt, muss immer zuerst der Elch eingesteckt sein und dann das Python Skript gestartet werden.

Elch_Bauteile_Schaltung

 

weihnachtsElch.ino

#include <Servo.h>
#include <EEPROM.h>
#include <float.h>

#include "prg.h"

static Servo gelenk;
static constexpr int gelenkPin = 2, nosePin = 3, keyPin = 4;
static unsigned long lastMillis;
static struct {
  int microSecondsTop, microSecondsBottom;
} config;
static float ti;
static bool keyPulse;
static float loop2T, loop2OldGel;
static prgPos myPrgPos;
static bool autoMode;

static int servoclamp(int v)
{
  if (v < 900) return 900;
  if (v > 2100) return 2100;
  return v;
}

// v bleibt in den Schranken l1,l2 wobei egal ist ob l1<l2 oder l1>l2
static float floatclamp(float v, float l1, float l2)
{
  float klein, gross;
  if (l1 < l2) {
    klein = l1;
    gross = l2;
  } else {
    klein = l2;
    gross = l1;
  }
  if (v < klein) v = klein;
  if (v > gross) v = gross;
  return v;
}

static void help()
{
  Serial.println(F(
    "FabLab Neuenstadt - Weihnachten 2022\n"
    "SuperELCH Programmieranleitung\n"
    "k: Kill automatic (zuerst machen, sonst geht nichts manuell)\n"
    "t,b: Fahre zu Top oder Bottom\n"
    "T,B: Merke aktuelle Position als Top oder Bottom\n"
    "a,e: Servo ausschalten / Servo einschalten\n"
    "1,2,3  viel,mittel,wenig nach links fahren\n"
    "4,5,6  wenig,mittel,viel nach rechts fahren\n"
    "w: aktuelle Konfiguration im EEPROM speichern\n"
    "h: Hilfe\n"
    "v: show values\n"));
}

static void loopComm()
{
  const int c = Serial.read();
  if (c=='t') gelenk.writeMicroseconds(servoclamp(config.microSecondsTop));
  if (c=='b') gelenk.writeMicroseconds(servoclamp(config.microSecondsBottom));
  if (c=='T') config.microSecondsTop=servoclamp(gelenk.readMicroseconds());
  if (c=='B') config.microSecondsBottom=servoclamp(gelenk.readMicroseconds());
  if (c=='e') gelenk.attach(gelenkPin);
  if (c=='a') gelenk.detach();
  if (c=='1') gelenk.writeMicroseconds(servoclamp(gelenk.readMicroseconds()+100));
  if (c=='2') gelenk.writeMicroseconds(servoclamp(gelenk.readMicroseconds()+10));
  if (c=='3') gelenk.writeMicroseconds(servoclamp(gelenk.readMicroseconds()+1));
  if (c=='4') gelenk.writeMicroseconds(servoclamp(gelenk.readMicroseconds()-1));
  if (c=='5') gelenk.writeMicroseconds(servoclamp(gelenk.readMicroseconds()-10));
  if (c=='6') gelenk.writeMicroseconds(servoclamp(gelenk.readMicroseconds()-100));
  if (c=='w') EEPROM.put(0, config);
  if (c=='k') autoMode = false;
  if (c=='h') help();
  if (c=='v') {
    Serial.print("Bottom=");
    Serial.print(config.microSecondsBottom);
    Serial.print(" Top=");
    Serial.print(config.microSecondsTop);
    Serial.print(" Current=");
    Serial.print(gelenk.readMicroseconds());
    Serial.print(" Prg=");
    Serial.println(myPrgPos.getProgramNumber());
    Serial.print("loop2OldGel=");
    Serial.println(loop2OldGel);
  }
}

void setup()
{
  Serial.begin(9600);

  pinMode(gelenkPin, OUTPUT);
  digitalWrite(gelenkPin, LOW);
  gelenk.attach(gelenkPin);
  pinMode(nosePin, OUTPUT);
  pinMode(keyPin, INPUT_PULLUP);

  EEPROM.get(0, config);
  if (config.microSecondsBottom <= 0 || config.microSecondsTop <= 0) {
    config.microSecondsBottom = servoclamp(1800);
    config.microSecondsTop = servoclamp(1600);
  }
  config.microSecondsBottom = servoclamp(config.microSecondsBottom);
  config.microSecondsTop = servoclamp(config.microSecondsTop);

  autoMode = true;

  lastMillis = millis();

  help();
}

static void gel(float p) // p=0 bottom / p=1 top
{
  p *= config.microSecondsTop - config.microSecondsBottom;
  p += config.microSecondsBottom;
  p = floatclamp(p, config.microSecondsBottom, config.microSecondsTop);
  gelenk.writeMicroseconds(p);
}

static void nos(float p) // p=0 bottom / p=1 top
{
  p *= 255;
  p = floatclamp(p, 0, 255);
  analogWrite(nosePin, p);
}

static void loopKey()
{
  static float pressed = 0;
  keyPulse = false;
  if ( ! digitalRead(keyPin)) {
    pressed += ti;
    if (pressed > 0.1) {
      keyPulse = true;
      pressed = -FLT_MAX;            
    }
  } else pressed = 0;
}

static void loopNoseServo()
{
  if (autoMode) {
    if (myPrgPos.currentAcceleration() > 0) {
      const float deltaA = ti * myPrgPos.currentAcceleration();
      loop2OldGel = deltaA * myPrgPos.currentServo() + loop2OldGel * (1.f - deltaA);
    } else {
      loop2OldGel = myPrgPos.currentServo();
    }
    gel(loop2OldGel);

    nos(myPrgPos.currentLed());

    if (loop2T > myPrgPos.currentDelay()) {
      loop2T -= myPrgPos.currentDelay();
      myPrgPos.nextCommand();
    }

    loop2T += ti;
  } else {
    nos(1);
  }
}

void loop()
{
  unsigned long nowMillis = millis();
  unsigned long diffMillis = nowMillis - lastMillis; // wraparound ergibt keinen Fehler.
  lastMillis = nowMillis;
  ti = diffMillis;
  ti /= 1000; // ti ist jetzt die Zeitdifferenz zum letzten Mal in Sekunden.

  loopKey(); // ztuerst machen, weil das keyPulse setzt.

  loopComm();

  loopNoseServo();
  
  if (keyPulse) {
    autoMode = true;
//    loop2OldGel = myPrgPos.currentServo() > 0.5 ? 0 : 1; // snap nose
    myPrgPos.nextProgram();
    loop2T = 0;
  }
}
prg.cpp

#include "prg.h"

#include <Arduino.h>
#include <float.h>

// Programme sind kodiert als Gelenk (0..1), Nase (0..1), Delay (s). Programmende mit einzelnem Eintrag FLT_MAX
// Der erste Wert jedes Programmes ist die Acceleration. negative acceleration = instant.
// Listenende mit zusätzlichen FLT_MAX Eintrag

static const float pStore[] PROGMEM = {
  ////////////////////////////////// 0
  2,
  0  , 0, 1,
  0  , 1, 1,
  0  , 0, 1,
  0.1, 1, 1,
  0  , 0, 1,
  0.2, 1, 1,
  0.1, 0, 1,
  0.3, 1, 1,
  0.2, 0, 1,
  0.4, 1, 1,
  0.3, 0, 1,
  0.5, 1, 1,
  0.4, 0, 1,
  0.6, 1, 1,
  0.5, 0, 1,
  0.7, 1, 1,
  0.6, 0, 1,
  0.8, 1, 1,
  0.7, 0, 1,
  0.9, 1, 1,
  0.8, 0, 1,
  1  , 1, 1,
  0.9, 0, 1,
  1  , 1, 1,
  1  , 0, 1,
  FLT_MAX,


  ////////////////////////////////// 1
  -1,
  0.9, 0, 0.5,
  1, 1, 0.5,
  0.9, 0, 0.5,
  1, 1, 0.5,
  0.4, 0, 1,
  1, 1, 30-0.5*4-1,
  FLT_MAX,
  

  ////////////////////////////////// 2
  -1,
  0.9,1,0.25,
  0.9,0,0.25,
  0.9,0,0.25,
  0.8,0,0.25,
  0.9,1,0.25,
  0.9,0,0.25,
  1,0,0.25,
  1,0,0.25,
  1,1,0.25,
  1,0,0.25,
  0.8,0,0.25,
  0.8,0,0.25,
  1,1,0.25,
  1,0,0.25,
  1,0,0.25,
  1,0,0.25,  
  
  1  ,1,0.25,
  1  ,0,0.25,
  1  ,0,0.25,
  1  ,0,0.25,
  1  ,0,0.25,
  1  ,0,0.25,
  1,  0,0.25,
  1,  0,0.25,
  1,  1,0.25,
  1,  0,0.25,
  1  ,0,0.25,
  1  ,0,0.25,
  1,  0,0.25,
  1,  0,0.25,
  1,  0,0.25,
  1,  0,0.25,  

  1  ,1,0.25,
  1  ,1,0.25,
  1  ,0,0.25,
  1  ,0,0.25,
  1  ,1,0.25,
  1  ,1,0.25,
  1,  0,0.25,
  1,  0,0.25,
  1,  1,0.25,
  1,  1,0.25,
  1  ,0,0.25,
  1  ,0,0.25,
  1,  1,0.25,
  1,  1,0.25,
  1,  0,0.25,
  1,  0,0.25,  

  1  ,1,0.25,
  1  ,0,0.25,
  1  ,1,0.25,
  1  ,0,0.25,
  1  ,1,0.25,
  1  ,0,0.25,
  1,  1,0.25,
  1,  0,0.25,
  1,  1,0.25,
  1,  0,0.25,
  1  ,1,0.25,
  1  ,0,0.25,
  1,  1,0.25,
  1,  0,0.25,
  1,  1,0.25,
  1,  0,0.25,  
  FLT_MAX,


  ////////////////////////////////// 3
  -1,
  1  ,1,0.2,
  0.8,1,0.1,
  1  ,1,0.2,
  0.8,1,0.1,
  1  ,1,0.2,
  0.8,1,0.2,
  0  ,0,20-0.2*4-0.1*2,
  FLT_MAX,


  ////////////////////////////////// 4
  1,
  1,0,10,
  0,1,50,
  FLT_MAX,


  ////////////////////////////////// 5
  -1,
  0,1,0.5,
  1,0,0.5,
  0,1,0.5,
  1,0,0.5,
  0,1,0.25,
  1,0,0.25,
  0,1,0.25,
  1,0,0.25,
  0,1,0.25,
  1,0,0.25,
  0,1,0.25,
  1,0,60-0.25*7-0.5*4,
  FLT_MAX,


  ////////////////////////////////// 6
  10,
  1.0,0,2,  
  1.0,1,2,  
  0.9,0,2,  
  0.9,1,2,  
  0.8,0,2,  
  0.8,1,2,  
  0.7,0,2,  
  0.7,1,2,  
  0.6,0,2,  
  0.6,1,2,  
  0.5,0,2,  
  0.5,1,2,  
  0.4,0,2,  
  0.4,1,2,  
  0.3,0,2,  
  0.3,1,2,  
  0.2,0,2,  
  0.2,1,2,  
  0.1,0,2,  
  0.1,1,2,  
  0  ,0,2,  
  0  ,1,2,  
  FLT_MAX,


  -1,
  0,0,1,
  1,1,1,
  FLT_MAX,


  -1,
  0,1,0.5,
  1,0,0.5,
  0.2,1,0.5,
  0.8,0,0.5,
  0.4,1,0.5,
  0.6,0,0.5,
  FLT_MAX,
  

  0.5,
  0,0,30,
  1,1,30,
  FLT_MAX,

  ////////////////////////////////// EOF
  FLT_MAX // end of programs!
};


prgPos::prgPos()
  : p(pStore+1), pCurrentProgram(pStore), programNumber(0)
{
}

void prgPos::nextProgram()
{
  while (pgm_read_float(p) != FLT_MAX) p+=3;
  if (pgm_read_float(++p) == FLT_MAX) {
    p = pStore + 1;
    pCurrentProgram = pStore;
    programNumber = 0;
  } else {
    ++programNumber;
    pCurrentProgram = p++;
  }
}

void prgPos::nextCommand()
{
  p+=3;
  if (pgm_read_float(p) == FLT_MAX) p = pCurrentProgram + 1;
}

float prgPos::currentLed()
{
  return pgm_read_float(p+1);
}

float prgPos::currentServo()
{
  return pgm_read_float(p);
}

float prgPos::currentDelay()
{
  return pgm_read_float(p+2);
}

float prgPos::currentAcceleration()
{
  return pgm_read_float(pCurrentProgram);
}

int prgPos::getProgramNumber()
{
  return programNumber;
}
prg.h

#ifndef __PRG_H
#define __PRG_H

class prgPos {
public:
  prgPos();
  void nextProgram();
  void nextCommand(); // auto wrap around
  float currentLed();
  float currentServo();
  float currentDelay();
  float currentAcceleration();
  int getProgramNumber();
private:
  const float *p, *pCurrentProgram;
  int programNumber;
};

#endif

Heißer Draht

kleiner heißer Draht (Kinderferienprogramm)

Für den leichteren Zusammenbau haben wir eine kleine Bauanleitung als PDF erstellt.

Benötigte Bauteile:

  • Platine oder Lochplatine nach Vorlage
  • Zwei Schalter
  • 9V Clip
  • Zwei 470 Ω Widerstände
  • Einen 220 Ω Widerstand
  • Summer
  • LED rot
  • LED grün
  • Bei Lochplatine Silberdraht
  • Holzbrett
  • 4 mm² Kupferkabel für den heißen Draht.
  • Schweißdraht (Stahl) für den Ösenhandgriff
  • Klingeldraht
  • Kabelkanal als Batterieclip
  • Schrauben zur Platinenbefestigung
  • Bügelperlen als Abstandshalter 😀

Benötigte Werkzeuge:

  • Lötkolben
  • Lötzinn
  • Dritte Hand
  • Pinzette
  • Seitenschneider
  • Schraubendreher

Projekt: Diskettenorgel

Die Diskettenorgel ist ein aus acht alten Diskettenlaufwerken bestehender MIDI Player, der über einen Raspberry Pi MiniPC MIDI Signale per USB-serieller Schnittstelle an einen Arduino UNO übergibt. Dieser steuert dann die einzelnen Schrittmotoren der alten Diskettenlaufwerke mit unterschiedlichen Geschwindigkeiten, abgeglichen zur Tonhöhe des Midifiles an.
Dadurch ist es möglich ganze Lieder abzuspielen und über das Touchdisplay die Wiedergabe zu steuern.

Weiterlesen

Projekt: LED Bügelbilder

Das gewünschte Bügelbild wird auf die Steckplatte mit den gewünschten Farben gesteckt, dort wo LED´s genutzt werden sollen, werden durchsichtige Bügelperlen eingesetzt. Eine Seite wird gebügelt. 3mm LEDs werden dann in den gewünschten Positionen auf der nicht gebügelten Seite eingesteckt und verlötet.
Der Vorwiderstand wird dann für 5V USB Spannung berechnet.

Weiterlesen

Projekt: LED Tisch

Der LED Tisch ist eine Matrix aus 12×12 RGB LEDs, welche über einen Teensy Mikrocontroller und mehrere Shields (USB, OctoWS2811, Arduino Proto) angesteuert werden.

Weiterlesen

Projekt: Miniorgel

Die Miniorgel ist eine astabile Kippstufe (Multivibrator), welche es ermöglicht 8 Töne über den Lautsprecher wiederzugeben.

Der hier gezeigte Prototyp ist noch in der Entwicklung für das Kinderferienprogramm 2016.

Weiterlesen

Projekt: Sanduhr

Ein Arduino Uno, welcher über 3 Servomotoren einen Griffel und einen Vibrationsmotor ansteuert.

Das Gehäuse ist aus gelasertem Plexiglas.

Weiterlesen

Projekt: Wordclock

Die Wordclock ist eine Uhr, die alle 5 Minuten die aktuelle Uhrzeit in Form von Worten anzeigt.

Die Uhr wurde im Rahmen eines Workshops des FabLab Nürnberg gebaut.

Weiterlesen