Laser engrave tools

For my winerack i engraved some wooden panels.

When doing so, i needed to fix the height of the engraver to get the focus of the beam right.

At start i removed all Z positions from the GCODE file after calibrating. Later i used a script wrote that fixed the height setting to 110.

#!/bin/bash
# Usage: confirm height focus at 110
# ./scriptname filetofix.gcode
myz=110
cat "$1" | sed s/Z1/Z${myz}/g | sed s/Z6/Z${myz}/g > "fixed.$1"

Another tool i made is the one below, it takes a GCODE file, calulates where the borders are (min/max x and y)
And sets the FAN2 (laser intensity to a minimum)
After that it generates GCODE to draw a box wherein the to be engraved object is made

Now you can run the GCODE file multiple times to position it on the wood to you can get the minimum of spoils.

#!/bin/bash
# Usage: scriptname file.gcode 
# It wil create a pointtest file for test running
myz=110
MAXX=$(cat "$1" | grep "G0 X" | awk '{ print $2 }' | cut -c2- | sort -n | tail -1)
MINX=$(cat "$1" | grep "G0 X" | awk '{ print $2 }' | cut -c2- | sort -n | head -1)
MAXY=$(cat "$1" | grep "G0 X" | awk '{ print $3 }' | cut -c2- | sort -n | tail -1)
MINY=$(cat "$1" | grep "G0 X" | awk '{ print $3 }' | cut -c2- | sort -n | head -1)

cat > "pointtest - $1" << EOF
;BingoStart
G90
M17 Z
G0 F3000
G0 Z${myz} F3000
M18 Z
G0 X${MINX} Y${MINY}
M106 S2
G0 F3000
G0 X${MINX} Y${MAXY}
M106 S2
G0 F3000
G0 X${MAXX} Y${MAXY}
M106 S2
G0 F3000
G0 X${MAXX} Y${MINY}
M106 S2
G0 F3000
G0 X${MINX} Y${MINY}
M106 S2
G0 F3000
G0 X${MINX} Y${MAXY}
M106 S2
G0 F3000
G0 X${MAXX} Y${MAXY}
M106 S2
G0 F3000
G0 X${MAXX} Y${MINY}
M106 S2
G0 F3000
G0 X${MINX} Y${MINY}
M106 S2
M107
;end
EOF

GCODES

  • M17 Z – disable Z movement
  • G0 – rapid move
    G0 X100 Y100 (Goto 100,100)
    G0 F3000 (speed)
  • M106 – set fan speed (I my case, this is laser intensity)
    M106 – S2
  • G90 – set absolute coordinates
  • M107 – fan off

More info about gcodes: http://www.science.smith.edu/cdf/pdf_files/Techno_GCODE%20Commands.pdf

3D printed double chanter proof of concept

We both got a smallpipe, so there are 2 chanters in the house.

I designed a 3D adaptor for two chanters, and 3D printed this.

Taping the bottom part of one of the chanters and a part of the other, gave me the possibillity to play harmony’s.

There are only a few tunes which are suitable for playing like this.

First recording after connecting the chanters and doing a 30sec tuning.

Blender model

Bluetooth Macro Keyboard for Photo Management

Arduino Bluetooth Photo view marco keyboard
  • Cursor pad on the left
  • 1 till 5 (see below)
  • star + 1-5, rates 1 till 5 stars
  • outline star – removes rating
  • bookmark + 1-5, color marks image
  • Triangle – start slideshow
  • zoom-in, rotate CCW, reset zoom, rotate CW, zoom-out
  • previous image, open image, fullscreen, exit fullscreen to manager and next image

I’ve used a esp32 with 18650 battery holder.

I still have to 3d print a case 🙂

Code:

#include <BleConnectionStatus.h>
#include <BleKeyboard.h>
#include <KeyboardOutputCallbacks.h>

#define DEBUG 0micro joystick
#define STAR 16
#define FLAG 17
#define COL1 18
#define COL2 19
#define COL3 21
#define COL4 22
#define COL5 23
#define COL6 25
#define ROW1 26
#define ROW2 27
#define ROW3 32
#define ROW4 33

int flagstate = 0;
int starstate = 0;
int row1state = 0;
int row2state = 0;
int row3state = 0;
int row4state = 0;
int col1state = 0;
int col2state = 0;
int col3state = 0;
int col4state = 0;
int col5state = 0;
int col6state = 0;

int colstate = 1;

BleKeyboard bleKeyboard;

void setup() {
#ifdef DEBUG
  Serial.begin(9600);
#endif
  bleKeyboard.begin();
  pinMode(STAR, INPUT_PULLUP);
  pinMode(FLAG, INPUT_PULLUP);
  pinMode(ROW1, INPUT_PULLUP);
  pinMode(ROW2, INPUT_PULLUP);
  pinMode(ROW3, INPUT_PULLUP);
  pinMode(ROW4, INPUT_PULLUP);
  pinMode(COL1, OUTPUT);
  pinMode(COL2, OUTPUT);
  pinMode(COL3, OUTPUT);
  pinMode(COL4, OUTPUT);
  pinMode(COL5, OUTPUT);
  pinMode(COL6, OUTPUT);
}
void loop() {
#ifdef DEBUG
  Serial.print("Colstate : ");
  Serial.print(colstate);
  Serial.print('\n');
#endif

  if (colstate == 1) {
    digitalWrite(COL1, LOW);
    digitalWrite(COL2, HIGH);
    digitalWrite(COL3, HIGH);
    digitalWrite(COL4, HIGH);
    digitalWrite(COL5, HIGH);
    digitalWrite(COL6, HIGH);
  }
  if (colstate == 2) {
    digitalWrite(COL1, HIGH);
    digitalWrite(COL2, LOW);
    digitalWrite(COL3, HIGH);
    digitalWrite(COL4, HIGH);
    digitalWrite(COL5, HIGH);
    digitalWrite(COL6, HIGH);
  }
  if (colstate == 3) {
    digitalWrite(COL1, HIGH);
    digitalWrite(COL2, HIGH);
    digitalWrite(COL3, LOW);
    digitalWrite(COL4, HIGH);
    digitalWrite(COL5, HIGH);
    digitalWrite(COL6, HIGH);
  }
  if (colstate == 4) {
    digitalWrite(COL1, HIGH);
    digitalWrite(COL2, HIGH);
    digitalWrite(COL3, HIGH);
    digitalWrite(COL4, LOW);
    digitalWrite(COL5, HIGH);
    digitalWrite(COL6, HIGH);
  }
  if (colstate == 5) {
    digitalWrite(COL1, HIGH);
    digitalWrite(COL2, HIGH);
    digitalWrite(COL3, HIGH);
    digitalWrite(COL4, HIGH);
    digitalWrite(COL5, LOW);
    digitalWrite(COL6, HIGH);
  }
  if (colstate == 6) {
    digitalWrite(COL1, HIGH);
    digitalWrite(COL2, HIGH);
    digitalWrite(COL3, HIGH);
    digitalWrite(COL4, HIGH);
    digitalWrite(COL5, HIGH);
    digitalWrite(COL6, LOW);
  }
  delay (100);
  flagstate = digitalRead(FLAG);
  starstate = digitalRead(STAR);
  row1state = digitalRead(ROW1);
  row2state = digitalRead(ROW2);
  row3state = digitalRead(ROW3);
  row4state = digitalRead(ROW4);
#ifdef DEBUG
  Serial.print("Rowstates : ");
  Serial.print(row1state);
  Serial.print(row2state);
  Serial.print(row3state);
  Serial.print(row4state);
  Serial.print('\n');
#endif
  // ROW1 = UP,DOWN,LEFT,RIGHT
  if (bleKeyboard.isConnected() && colstate == 1) {
    // UP
#ifdef DEBUG
    Serial.print("Up Pressed ");
    Serial.print('\n');
#endif

    if (row1state == 0) {
      bleKeyboard.press(KEY_UP_ARROW);
      delay (100);
      bleKeyboard.releaseAll();
    }
    // DOWN
    if (row2state == 0) {
      bleKeyboard.press(KEY_DOWN_ARROW);
      delay (100);
      bleKeyboard.releaseAll();
    }
    // LEFT
    if (row3state == 0) {
      bleKeyboard.press(KEY_LEFT_ARROW);
      delay (100);
      bleKeyboard.releaseAll();
    }
    // RIGHT
    if (row4state == 0) {
      bleKeyboard.press(KEY_RIGHT_ARROW);
      delay (100);
      bleKeyboard.releaseAll();
    }
  }
  // ROW2 = (1),(star),ZOOMIN,PREVIOUS
  if (bleKeyboard.isConnected() && colstate == 2) {
    // 1 - star
    if (row1state == 0 && starstate == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press('1');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // 1 - flag
    if (row1state == 0 && flagstate == 0) {
      bleKeyboard.press(KEY_LEFT_ALT);
      bleKeyboard.press('1');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // NO ROWSTATE2

    // zoom in
    if (row3state == 0) {
      bleKeyboard.press('+');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // PREVIOUS
    if (row4state == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press(KEY_LEFT_ARROW);
      delay (100);
      bleKeyboard.releaseAll();
    }
  }
  // ROW3 = (2),unstar,CCWrotate,open
  if (bleKeyboard.isConnected() && colstate == 3) {
    // 2 - star
    if (row1state == 0 && starstate == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press('2');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // 2 - flag
    if (row1state == 0 && flagstate == 0) {
      bleKeyboard.press(KEY_LEFT_ALT);
      bleKeyboard.press('2');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // unstar
    if (row2state == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press('0');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // CCW rotate
    if (row3state == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press(KEY_LEFT_ALT);
      bleKeyboard.press(KEY_LEFT_ARROW);
      delay (100);
      bleKeyboard.releaseAll();
    }
    // open
    if (row4state == 0) {
      bleKeyboard.press(KEY_RETURN);
      delay (100);
      bleKeyboard.releaseAll();
    }
  }

  // ROW4 = (3),(flag),zoom,fullscreen
  if (bleKeyboard.isConnected() && colstate == 4) {
    // 3 - star
    if (row1state == 0 && starstate == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press('3');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // 3 - flag
    if (row1state == 0 && flagstate == 0) {
      bleKeyboard.press(KEY_LEFT_ALT);
      bleKeyboard.press('3');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // NO ROWSTATE2

    // zoom reset
    if (row3state == 0) {
      bleKeyboard.press('*');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // fullscreen
    if (row4state == 0) {
      bleKeyboard.press('f');
      delay (100);
      bleKeyboard.releaseAll();
    }
  }

  // ROW5 = (4),unflag,CWrotate,exit
  if (bleKeyboard.isConnected() && colstate == 5) {
    // 4 - star
    if (row1state == 0 && starstate == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press('4');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // 4 - flag
    if (row1state == 0 && flagstate == 0) {
      bleKeyboard.press(KEY_LEFT_ALT);
      bleKeyboard.press('4');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // unflag
    if (row2state == 0) {
      bleKeyboard.press(KEY_LEFT_ALT);
      bleKeyboard.press('0');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // CW rotate
    if (row3state == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press(KEY_LEFT_ALT);
      bleKeyboard.press(KEY_RIGHT_ARROW);
      delay (100);
      bleKeyboard.releaseAll();
    }
    // exit
    if (row4state == 0) {
      bleKeyboard.press(KEY_ESC);
      delay (100);
      bleKeyboard.releaseAll();
    }
  }

  // ROW6 = (5),slideshow,zoomout,next
  if (bleKeyboard.isConnected() && colstate == 6) {
    // 5 - star
    if (row1state == 0 && starstate == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press('5');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // 5 - flag
    if (row1state == 0 && flagstate == 0) {
      bleKeyboard.press(KEY_LEFT_ALT);
      bleKeyboard.press('5');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // slideshow
    if (row2state == 0) {
      bleKeyboard.press(KEY_ESC);
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press('s');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // zoom out
    if (row3state == 0) {
      bleKeyboard.press('-');
      delay (100);
      bleKeyboard.releaseAll();
    }
    // next
    if (row4state == 0) {
      bleKeyboard.press(KEY_LEFT_CTRL);
      bleKeyboard.press(KEY_RIGHT_ARROW);
      delay (100);
      bleKeyboard.releaseAll();
    }
  }
  colstate++;
  if (colstate == 7) {
    colstate = 1;
  }
}
Keyboard layout

Laser engraving

Bought a laser engraving kit, which can be mounted on my 3D Creality Printer, using magnets.

I’ve connected the power to a fan connector in 3D printer. So it can be controlled with gcode’s

M18 Z; stop Z axes, use only X and Y
M106 ; laser on command
M107 ; laser stop command

I’ve created a bash script which outlines our design with minimal and maximal x and y coordinates, so you can allign your object just in the right place.
It uses a low voltage on you laser, so its visible but it doesn’t burn you object.

#!/bin/bash
myz=100
MAXX=$(cat "$1" | grep "G0 X" | awk '{ print $2 }' | cut -c2- | sort -n | tail -1)
MINX=$(cat "$1" | grep "G0 X" | awk '{ print $2 }' | cut -c2- | sort -n | head -1)
MAXY=$(cat "$1" | grep "G0 X" | awk '{ print $3 }' | cut -c2- | sort -n | tail -1)
MINY=$(cat "$1" | grep "G0 X" | awk '{ print $3 }' | cut -c2- | sort -n | head -1)

cat > "pointtest - $1" << EOF
;BingoStart
G90
M17 Z
G0 F3000
G0 $myz F3000
M18 Z
G0 X${MINX} Y${MINY}
M106 S2
G0 F3000
G0 X${MINX} Y${MAXY}
M106 S2
G0 F3000
G0 X${MAXX} Y${MAXY}
M106 S2
G0 F3000
G0 X${MAXX} Y${MINY}
M106 S2
G0 F3000
G0 X${MINX} Y${MINY}
M106 S2
G0 F3000
G0 X${MINX} Y${MAXY}
M106 S2
G0 F3000
G0 X${MAXX} Y${MAXY}
M106 S2
G0 F3000
G0 X${MAXX} Y${MINY}
M106 S2
G0 F3000
G0 X${MINX} Y${MINY}
M106 S2
M107
;end
EOF

Below a offset fixer

#!/bin/bash
myz=100
cat "$1" | sed s/Z1/Z${myz}/g | sed s/Z6/Z${myz}/g > "fixed.$1"

Internals of my Creality 3D Printer .. using fan controller to power/control engraving laser

Repairing my Concertina

UPDATE: 7 Jan 2023 – Another repair

15 Feb of this year my Concertina was deliverd on my doorstep.

It is a Wren 2 Anglo C/G Concertina by McNeela. A affordable good instrument.

Features

  • 30 button Anglo Concertina in the key of C/G
  • Fully chromatic
  • Measures 6.3″ across the flats (16cm)
  • 8 fold bellows
  • Steel reeds
  • Loud, crisp tone
  • Durable black buttons
  • Wheatstone/Lachenal layout
  • Excellent response
  • Easy to play
  • Lightweight concertina – easy to handle for all ages
  • Traditional size – unlike other bulky beginner concertinas
  • Softer, more comfortable, adjustable hand-straps – suitable for both children and adults
  • Finished with improved stylish gloss exposed-grain wood
  • Comes with a complimentary Concertina Padded Hard Case 

The word concertina refers to a family of hand-held bellows-driven free reed instruments constructed according to various systems, which differ in terms of keyboard layout, and whether individual buttons (keys) produce the same (unisonoric) or different (bisonoric) notes with changes in the direction of air pressure.
It is weird playing two notes with the same button, but i gives you a broader tonal range. Some notes are double, one on the pull action and one on the push action. When playing for a while it is possible to switch between those two to be able to play faster.
Because you can use the same pull or push as you already where playing, or the button is close by the one you already play.

A little diagram i drew myself using inkscape to learn the buttons i needed. (Blanks also have notes)

So i had a little problem with the pull motion of the right hand A’ B’.
I didn’t want to send my Concertina back (Still has garantee, i know)
But sending it back to Ireland will take a while and has postal costs.
(I’ve fixed my Accordeon also, so why not try to fix it myself?)
(… aaand i was curious of the insides )

I had to look at the inner workings for a minute, and finding the right reed.
The middle picture above shows a white plastic strip, one of them was stuck. Moving it a little and carefully bending it, got it free and moveable again.

On with the lessons from Caitlin Nic Gabhann again!
https://www.youtube.com/watch?v=Z3tPzc6kKM0

2023 Repair:

One of the reeds fell off! It was not glued properly. A 5 minute fix!

Hex Dimmer

Control a dimmer using a hex wireless box.

Parts

  • Wemos Mini
  • MPU6050 – Gyroscope Module
  • 10k Resistor
  • TP4056 – Battery Charger Module
  • Mini Battery
  • Wireless Charger

Put the box flat on the table to switch off.
When you put it on one side, it will controll your lights brightness.
20,40,60,80 and 100%, just by rotating and putting it down on its sides.

3D printed case

Schematics (without the wireless charging part)

Wireless part

Node-Red Controll part (source below)

Nice to have’s :
Arduino-sleep mode, wakeup with a movement sensor.

Arduino Code

#include <Wire.h>
//#include <SPI.h>
#include <PubSubClient.h>
//#include <string.h>
//#include <stdio.h>
#include <ESP8266WiFi.h>

// Wifi settings
const char* ssid = "MTAP1";
const char* password = "xxxxxxxxxx";
const char* mqtt_server = "10.1.0.17";

// I2C address of the MPU-6050 - 0x68 or 0x69 if AD0 is pulled HIGH
const int MPU = 0x68;
int16_t AcX, AcY, AcZ, GyX, GyY, GyZ;
float gForceX, gForceY, gForceZ, rotX, rotY, rotZ;

// Wifi MAC address
byte mac[]= {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };

WiFiClient espClient;
IPAddress ip;
PubSubClient mqttClient(espClient);

// IP address of your MQTT server
const char* server = "10.1.0.17";
//const char* outTopic = "test/";
//const char* server = "iot.eclipse.org";

void dataReceiver(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,true);  // request a total of 14 registers
  AcX = Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY = Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ = Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  GyX = Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY = Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ = Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  processData();
}

void processData(){
  gForceX = AcX / 16384.0;
  gForceY = AcY / 16384.0; 
  gForceZ = AcZ / 16384.0;
  
  rotX = GyX / 131.0;
  rotY = GyY / 131.0; 
  rotZ = GyZ / 131.0;
}

void debugFunction(int16_t AcX, int16_t AcY, int16_t AcZ, int16_t GyX, int16_t GyY, int16_t GyZ){
  // Print the MPU values to the serial monitor
  Serial.print("Accelerometer: ");
  Serial.print("X="); Serial.print(gForceX);
  Serial.print("|Y="); Serial.print(gForceY);
  Serial.print("|Z="); Serial.println(gForceZ);  
  Serial.print("Gyroscope:");
  Serial.print("X="); Serial.print(rotX);
  Serial.print("|Y="); Serial.print(rotY);
  Serial.print("|Z="); Serial.println(rotZ);
}

void reconnect() {
  // Loop until we're reconnected
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (mqttClient.connect("arduinoClient")){
      Serial.println("connected");
    } 
    else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
//      Wait 5 seconds before retrying
      delay(1000);
    }
  }
}

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

    setup_wifi();
  
  Wire.begin(0,2);
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  mqttClient.setServer(server, 1883);
  
//  Ethernet.begin(mac);
//  ip = Ethernet.localIP();
  
  Serial.println(ip);  
  Serial.println(server);
  //delay(1500);
}

char* init(float val){
  
  char buff[100];

  for (int i = 0; i < 100; i++) {
      dtostrf(val, 4, 2, buff);  //4 is mininum width, 6 is precision
  }
   return buff;

}

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void dataAcc(){

  char mpu6050X[100]= "";   
  strcat(mpu6050X,init(gForceX));

  char mpu6050Y[100]= "";   
  strcat(mpu6050Y,init(gForceY));

  char mpu6050Z[100]= "";   
  strcat(mpu6050Z,init(gForceZ));

  // accelerometer - "topic, mpu6050"
  mqttClient.publish("AcX/", mpu6050X);
  mqttClient.publish("AcY/", mpu6050Y);
  mqttClient.publish("AcZ/", mpu6050Z);
//  mqttClient.publish(outTopic, "text to send via mqtt");
}


void dataGy(){

  char mpu6050X[100]= "";
  strcat(mpu6050X,init(rotX));

  char mpu6050Y[100]= "";
  strcat(mpu6050Y,init(rotY));

  char mpu6050Z[100]= "";
  strcat(mpu6050Z,init(rotZ));
  
  // gyroscope - "topic, mpu6050"
  mqttClient.publish("GyX/", mpu6050X);
  mqttClient.publish("GyY/", mpu6050Y);
  mqttClient.publish("GyZ/", mpu6050Z);
//  mqttClient.publish(outTopic, "text to send via mqtt");
}

void loop(){
  dataReceiver();
  debugFunction(AcX,AcY,AcZ,GyX,GyY,GyZ);

  if (!mqttClient.connected()) {
    reconnect();
  }

  mqttClient.loop(); 

  dataAcc();
  dataGy();

  delay(2000);
}

Nodered Flow

[
    {
        "id": "7550958a.b29dec",
        "type": "mqtt in",
        "z": "a0126a6a.9c70b8",
        "name": "",
        "topic": "hex/x",
        "qos": "2",
        "broker": "8c74c5f6.9a7a48",
        "x": 270,
        "y": 100,
        "wires": [
            [
                "d251dd79.5700d"
            ]
        ]
    },
    {
        "id": "e84b0a1.18096f8",
        "type": "mqtt in",
        "z": "a0126a6a.9c70b8",
        "name": "",
        "topic": "hex/y",
        "qos": "2",
        "broker": "8c74c5f6.9a7a48",
        "x": 270,
        "y": 180,
        "wires": [
            [
                "9c27bc8f.b62dd"
            ]
        ]
    },
    {
        "id": "6a1a0d8d.b3e754",
        "type": "mqtt in",
        "z": "a0126a6a.9c70b8",
        "name": "",
        "topic": "hex/z",
        "qos": "2",
        "broker": "8c74c5f6.9a7a48",
        "x": 270,
        "y": 260,
        "wires": [
            []
        ]
    },
    {
        "id": "2d2a911a.6af3fe",
        "type": "ui_gauge",
        "z": "a0126a6a.9c70b8",
        "name": "",
        "group": "d43a9f25.6c874",
        "order": 23,
        "width": 0,
        "height": 0,
        "gtype": "gage",
        "title": "gauge",
        "label": "units",
        "format": "{{value}}",
        "min": "0",
        "max": "100",
        "colors": [
            "#00b500",
            "#e6e600",
            "#ca3838"
        ],
        "seg1": "",
        "seg2": "",
        "x": 1010,
        "y": 120,
        "wires": []
    },
    {
        "id": "d251dd79.5700d",
        "type": "function",
        "z": "a0126a6a.9c70b8",
        "name": "Get level from box",
        "func": "var my=msg.payload;\nmsg.payload = {};\nif (my == 0.85){\n    msg.payload=20;\n    return msg;\n}\nelse if (my == 0.86){\n    msg.payload=20;\n    return msg;\n}\nelse if (my == 0.87){\n    msg.payload=20;\n    return msg;\n}\n\nelse if (my == 0.03){\n    msg.payload=40;\n    return msg;\n}\nelse if (my == 0.02){\n    msg.payload=40;\n    return msg;\n}\n\nelse if (my == 3.17){\n    msg.payload=60;\n    return msg;\n}\nelse if (my == 3.18){\n    msg.payload=60;\n    return msg;\n}\n\nelse if (my == 0.04){\n    msg.payload=80;\n    return msg;\n}\nelse if (my == 0.05){\n    msg.payload=80;\n    return msg;\n}\n\nelse if (my == 3.95){\n    msg.payload=100;\n    return msg;\n}\nelse if (my == 3.96){\n    msg.payload=100;\n    return msg;\n}\nelse {\n    return msg;\n    \n}\n",
        "outputs": 1,
        "noerr": 0,
        "x": 510,
        "y": 120,
        "wires": [
            [
                "ecd746cc.fce348",
                "8721e902.45d8b8",
                "39c8f1ac.86affe"
            ]
        ]
    },
    {
        "id": "39c8f1ac.86affe",
        "type": "function",
        "z": "a0126a6a.9c70b8",
        "name": "Set Living spots level (idx 5)",
        "func": "var level = Number(msg.payload);\nmsg.payload = {};\nmsg.payload.idx = 5;\nmsg.payload.switchcmd = (\"Set Level\");\nmsg.payload.command = \"switchlight\";\nmsg.payload.level = level;\nreturn msg;  ",
        "outputs": 1,
        "noerr": 0,
        "x": 820,
        "y": 260,
        "wires": [
            [
                "bc0d6507.1d7748"
            ]
        ]
    },
    {
        "id": "bc0d6507.1d7748",
        "type": "mqtt out",
        "z": "a0126a6a.9c70b8",
        "name": "",
        "topic": "domoticz/in",
        "qos": "",
        "retain": "",
        "broker": "8c74c5f6.9a7a48",
        "x": 1080,
        "y": 260,
        "wires": []
    },
    {
        "id": "9c27bc8f.b62dd",
        "type": "function",
        "z": "a0126a6a.9c70b8",
        "name": "Flat or standing up",
        "func": "var mya=msg.payload;\nmsg.payload = {};\nif (mya < -3.80){\n    flow.set(\"levely\",1);\n    msg.payload  = \"plat\";\n        }\nelse {\n    flow.set(\"levely\",2);\n    msg.payload  = \"rechtop\";\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 450,
        "y": 200,
        "wires": [
            [
                "ecd746cc.fce348"
            ]
        ]
    },
    {
        "id": "ecd746cc.fce348",
        "type": "debug",
        "z": "a0126a6a.9c70b8",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 640,
        "y": 340,
        "wires": []
    },
    {
        "id": "8721e902.45d8b8",
        "type": "function",
        "z": "a0126a6a.9c70b8",
        "name": "Gate for level ",
        "func": "\nvar x = msg.payload;\ny = flow.get(msg.payload);\nvar y = flow.get('levely') || 0;\n\nif (y == 1){\n    msg.payload = {};\n        msg.payload = 0;\n} else {\n    msg.payload = x;\n}\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 810,
        "y": 120,
        "wires": [
            [
                "2d2a911a.6af3fe",
                "da72437e.88376"
            ]
        ]
    },
    {
        "id": "da72437e.88376",
        "type": "debug",
        "z": "a0126a6a.9c70b8",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 890,
        "y": 200,
        "wires": []
    },
    {
        "id": "8c74c5f6.9a7a48",
        "type": "mqtt-broker",
        "z": "",
        "name": "10.1.0.17",
        "broker": "10.1.0.17",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "15",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    },
    {
        "id": "d43a9f25.6c874",
        "type": "ui_group",
        "z": "",
        "name": "Control",
        "tab": "739541e2.18396",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "739541e2.18396",
        "type": "ui_tab",
        "z": "",
        "name": "7inch",
        "icon": "dashboard",
        "order": 1,
        "disabled": false,
        "hidden": false
    }
]

Mattermost Mqtt Flag Integration

UPDATE 20231020 – Via USB Serial (also schematic for below integration)
https://www.henriaanstoot.nl/2023/10/20/thunderbird-mail-notification-flag-via-usb/

Using a servo which is MQTT controlled, and a slash command in Mattermost, i can be notified by friends and colleages.

Flag, and monitor-stand are 3D printed.

ESP is a simple wemos mini.

Note the slash command, M5 stick flow is unrelated!

Laser pointer web controlled

Coline sometimes plays games with her sisters online using my streaming server. (Due to Covid)

Some games are difficult because you have to point to a location on the table. So i came up with a laser pointer solution.

The idea is: Send a url to the players with a streaming camera, you see the game on your screen but you can click on a location on the screen to move a laser pointer to that location.

Below is a proof of concept using the Lasercut worldmap on the wall and a website with a worldmap.

Laser pointer module

Code to place on the ESP:

This will connect to the MQTT broker and listens for messages on the servo/pan and tilt topic.

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Servo.h> 
Servo pan; 
Servo tilt;

const char* ssid = "MYSSI";                
const char* password = "MYWIFIPASS";
const char* mqtt_server = "IPMQTTBROKER";
const char* topic_pan = "servo/pan";
const char* topic_tilt = "servo/tilt";
 
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {
  pan.attach(D5);
  tilt.attach(D6);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {
  delay(5);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
 String string;
 for (int i = 0; i < length; i++) {
 string+=((char)payload[i]); 
 }
 int pos = string.toInt(); 
 if ( strcmp(topic, topic_pan) == 0 ) {
 pan.write(pos); 
 }
 if ( strcmp(topic, topic_tilt) == 0 ) {
 tilt.write(pos); 
 }
 delay(15); 
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect("ESP8266Client")) {
      client.subscribe(topic_pan); 
      client.subscribe(topic_tilt); 
    } else {
      delay(5000);
    }
  }
}
void loop() {   
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  delay(100);
}

Website PHP code:

This has some calibration code to get coordinates lined-up

<?Php
$foo_x=$_POST['foo_x'];
$foo_y=$_POST['foo_y'];
echo "X=$foo_x, Y=$foo_y ";
$x=160 - round($foo_x/30);
$y=38 - round($foo_y/100);

system('/usr/bin/mosquitto_pub -h 10.1.0.17 -t servo/pan -m "' . $x . '"');
system('/usr/bin/mosquitto_pub -h 10.1.0.17 -t servo/tilt -m "' . $y . '"');
?>
<form action='' method=post>
<input type="image" alt=' Finding coordinates of an image' src="worldmap.jpg"
name="foo" style=cursor:crosshair;/>
</form>

POC

"If something is worth doing, it's worth overdoing."