HLK-LD2410B Mqtt socket and canvas

Follow up on yesterday’s post

Using a html page with javascript, I made a proof of concept displaying realtime information from the sensor.

The sensor is active using a Home Assistant integration.
https://www.henriaanstoot.nl/2022/11/07/home-assistant-nodered-update/

But using the Node-red integration, i take the payload and write this to a mqtt topic

The HTML page below reads the topic using the websocket configured in mosquitto and draws the distance using canvas

cat /etc/mosquitto/conf.d/websockets.conf
listener 9001
protocol websockets
allow_anonymous true


Distance drawn using canvas. Little dividers on top are meters

HTML PAGE with javascript

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
 	<script type = "text/javascript" 
         src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script type = "text/javascript">
	var connected_flag=0	
	var mqtt;
    var reconnectTimeout = 2000;
	var host="MQTTSERVER";
	var port=9001;
var sub_topic="web/#";
	function onConnectionLost(){
	console.log("connection lost");
	document.getElementById("status").innerHTML = "Connection Lost";
	document.getElementById("messages").innerHTML ="Connection Lost";
	connected_flag=0;
	}
	function onFailure(message) {
		console.log("Failed");
		document.getElementById("messages").innerHTML = "Connection Failed- Retrying";
        setTimeout(MQTTconnect, reconnectTimeout);
        }
	function onMessageArrived(r_message){
		out_msg="Message received "+r_message.payloadString+"<br>";
		//out_msg=out_msg+"Message received Topic "+r_message.destinationName;
		//console.log("Message received ",r_message.payloadString);
		console.log(out_msg);
		document.getElementById("messages").innerHTML =out_msg;
		var topic=r_message.destinationName;
		if(topic=="web/module1")
		{
		document.getElementById("module1").innerHTML =r_message.payloadString;
		}
		if(topic=="web/module2")
		{
		document.getElementById("module2").innerHTML =r_message.payloadString;
		
		var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var centerX = 10;
context.clearRect(0, 0, 1800, 1000);
var centerY = 10;
var radius = r_message.payloadString;

let circle = new Path2D();  // 
circle.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);

//context.fillStyle = 'white';

context.fillStyle = "rgba(255, 255, 255, 0.2)";
context.fill(circle); //   

context.lineWidth = 5;
context.strokeStyle = '#000066';
context.stroke(circle);  //

// top line
context.beginPath();
context.moveTo(10, 10);
context.lineTo(1500, 10);
context.stroke();

// 3x dividers		
context.beginPath();
context.moveTo(400, 0);
context.lineTo(400, 20);
context.stroke();
		
context.beginPath();
context.moveTo(800, 0);
context.lineTo(800, 20);
context.stroke();
		
context.beginPath();
context.moveTo(1200, 0);
context.lineTo(1200, 20);
context.stroke();
		
		}
		}
	function onConnected(recon,url){
	console.log(" in onConnected " +reconn);
	}
	function onConnect() {
	  // Once a connection has been made, make a subscription and send a message.
	document.getElementById("messages").innerHTML ="Connected to "+host +"on port "+port;
	connected_flag=1
	document.getElementById("status").innerHTML = "Connected";
	console.log("on Connect "+connected_flag);
	mqtt.subscribe(sub_topic);
	  }

    function MQTTconnect() {

	console.log("connecting to "+ host +" "+ port);
	var x=Math.floor(Math.random() * 10000); 
	var cname="controlform-"+x;
	mqtt = new Paho.MQTT.Client(host,port,cname);
	//document.write("connecting to "+ host);
	var options = {
        timeout: 3,
		onSuccess: onConnect,
		onFailure: onFailure,
      
     };
	
        mqtt.onConnectionLost = onConnectionLost;
        mqtt.onMessageArrived = onMessageArrived;
		//mqtt.onConnected = onConnected;

	mqtt.connect(options);
	return false;
  
 
	}
	function sub_topics(){
		document.getElementById("messages").innerHTML ="";
		if (connected_flag==0){
		out_msg="<b>Not Connected so can't subscribe</b>"
		console.log(out_msg);
		document.getElementById("messages").innerHTML = out_msg;
		return false;
		}
	var stopic= document.forms["subs"]["Stopic"].value;
	console.log("Subscribing to topic ="+stopic);
	mqtt.subscribe(stopic);
	return false;
	}
	function send_message(msg,topic){
		if (connected_flag==0){
		out_msg="<b>Not Connected so can't send</b>"
		console.log(out_msg);
		document.getElementById("messages").innerHTML = out_msg;
		return false;
		}
		var value=msg.value;
		console.log("value= "+value);
		console.log("topic= "+topic);
		message = new Paho.MQTT.Message(value);
		message.destinationName = "web/"+topic;

		mqtt.send(message);
		return false;
	}

	
    </script>

  </head>
  <body onload="MQTTconnect()">
	

 <table>
<tr><td>Sensor1:<td><td  id="module1"><td><td >
<tr><td>Sensor2:</td><td  id="module2"><td></tr>

</table>
<div id="status">Connection Status: Not Connected</div>
</div>
<br>

Messages:<p id="messages"></p>
     <canvas id="canvas" width="1600" height="1000"></canvas>  
  </body>
</html>

Got some new sensors (HLK-LD2410B)

Sometimes you forget you ordered something from Ali Express, it takes too long to arrive.

Today i’ve got this in my mailbox

These are HLK-LD2401B motion/presence detectors € 2,91 a piece.

While PIR sensors are slow and doing only motion sensing, these nice small and cheap devices are fast and have more outputs.

  • Bluetooth (can be used using home assistant integration)
  • Motion and presence
  • 60 degrees detection angle
  • Measurements to moving/static objects (while the datasheet mentions till 5 meter, i’ve got measurements well above that.
  • Fast updates, and i mean really fast
  • Only 7mm x 35mm
  • mmWave – 24GHz
  • GPIO Uart

According to the bad translation it can also measure if you ‘devour’ something. Dutch ‘vreten’ means wild fast eating something.
Setting the language to English gives me the word ‘fretting’

Right screenshot shows Coline sitting at a distance of 5.5 meters

Above, the update speed in HA

To try: connect Uart to remote ESP
and tweaking the device
https://www.youtube.com/watch?v=dAzHXpP3FcI
and distance gates

Maybe i can use some Triangulation go find the precise location of a person.

(Blind) Maze puzzle now with generation and play modes.

Info:

  • PlatformIO tested ( install StackArray.h in lib/StackArray/StackArray.h )
    • Reset button and dipswitches for below options
      • Visited pathmode ( dip switch 1)
        • Preview maze generation ( dip switch 3)
        • Hard mode – starts at first position when hitting a wall ( dip 2 )
        • Longest path mode (longest stackarray before stack.pop (dip 4 )
        • Prints serial info, like a drawn maze
        • Pixel remapping for other displays ( Below mapping i had to use, see source sketch )

How is the maze generated?

It is generated using recursive backtracking:

Start at 0,0 mark visited, select a random direction from unvisited neighbours, push current on the stack, goto new location, and mark this visited.
Repeat until no possible directions.
pop from the stack a previous location, another direction possible? Take that path and repeat.
Repeat popping from stack, until stack empty .. now all cells are visited

longest pop : 41
maze
------------------------
|    ||    ||       || |
---            ---      
| ||    ||    ||    || |
   ------------   ---   
|       || ||    ||    |
   ---         ---   ---
|    ||    || ||       |
      ---         ---
| || || || || || ||    |

| || || || || || || || |
---
|    ||    || || || || |
   ---------   ---
|                   || |
------------------------
x y : 00
maze 4
|4|3|6|3|6|5|3|2|
|2|12|9|12|9|6|9|10|
|14|5|3|2|6|9|6|9|
|14|3|12|11|10|6|13|3|
|10|10|2|10|10|10|6|11|
|8|10|10|10|10|10|10|10|
|6|9|12|9|10|8|10|10|
|12|5|5|5|13|5|9|8|

Easiest play mode: Preview Maze, Show visited path, NO reset to square 0,0 and NOT the longest path so :

Show visited DIP1Reset to 0,0 DIP2Preview DIP3Longest path DIP4
1010

Hard mode:

Show visited DIP1Reset to 0,0 DIP2Preview DIP3Longest path DIP4
0101

Todo: Battery / batterymanager / 3D case / better buttons

Some video shorts put together to show modes

CODE

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include "StackArray.h"

// Pin Assign
int up=25;
int down=33;
int left=32;
int right=26;
int trailsw=17;
int restartsw=5;
int showmazesw=18;
int popendsw=19;

int xend=7;
int yend=7;

int countpop=0;
int currentpop=0;

int directions[4]{};
int notalldone = 1;
int tmpx=0;
int tmpy=0;
int xgen = 1;
int ygen = 1;
int x = 0;
int y = 0;
int showmaze = 0;

// 0 easy = trail // 1 only red walls // 2 = reset to 0.0
int mode=0;

//int trail=32;
int trail=32;

// Which pin on the Arduino is connected to the NeoPixels?
#define LED_PIN    2

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 64

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

int maze[8][8] = {

  };

int displaymatrix[8][8] = { 
{ 0,1,2,3,4,5,6,7 },
{ 15,14,13,12,11,10,9,8 }, 
{16,17,18,19,20,21,22,23},
{31,30,29,28,27,26,25,24},
{32,33,34,35,36,37,38,39},
{47,46,45,44,43,42,41,40},
{48,49,50,51,52,53,54,55},
{63,62,61,60,59,58,57,56}
};

int visitmatrix[10][10] = { 
  1,1,1,1,1,1,1,1,1,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,1
  };


void(* resetFunc) (void) = 0;

void drawmaze2(){
  Serial.print("x y : ");
 Serial.print(x);
 Serial.println(y);
 Serial.print("maze ");
 Serial.println(maze[x][y]);
  for(int ledy=0;ledy<8;ledy++)
  { Serial.print("|");
    for(int ledx=0;ledx<8;ledx++){
    Serial.print(maze[ledx][ledy]);  
    Serial.print("|");  
    if ( maze[ledx][ledy] != 0 ) {
       // mled.dot(ledx,ledy); // draw dot
      //  mled.display();
       // delay(50);
    }
      }  
    Serial.println("");
  }
  Serial.println("");
  delay(100);
}

void drawmaze(){
  Serial.println("maze ");
 
  for(int ledy=0;ledy<8;ledy++)
  { 
    // 1st line
    for(int ledx=0;ledx<8;ledx++){
    if(bitRead(maze[ledx][ledy], 3) == 0){ Serial.print("---"); } else { Serial.print("   "); }
    }
    Serial.println("");
    // 2nd line

    for(int ledx=0;ledx<8;ledx++){
    if(bitRead(maze[ledx][ledy], 0) == 1){
        if(bitRead(maze[ledx][ledy], 2) == 1){
          Serial.print("   "); } 
          else 
          { Serial.print("  |"); }
     }
     if(bitRead(maze[ledx][ledy], 0) == 0){
        if(bitRead(maze[ledx][ledy], 2) == 1){
          Serial.print("|  "); } 
          else 
          { Serial.print("| |"); }
     }
    

    
    }
        Serial.println("");
} 
   
    // last line
    int ledy=7;
    for(int ledx=0;ledx<8;ledx++){
    if(bitRead(maze[ledx][ledy], 1) == 0){ Serial.print("---"); } else { Serial.print("   "); }
    }
    Serial.println("");

    
    
  
  //Serial.println("");
  delay(100);
}

void mazegen(){
  visitmatrix[xgen][ygen]=1;       
  StackArray <int> rowStack; 
  StackArray <int> colStack;
        rowStack.push(xgen);
        colStack.push(ygen);
  while(notalldone == 1){    
     visitmatrix[xgen][ygen]=1;
        while(!rowStack.isEmpty()) {
        int count=0;
        //up
        if ( visitmatrix[xgen-1][ygen] == 0 ){
          directions[count]=1;
          count++;    
        }
        //right
        if ( visitmatrix[xgen][ygen+1] == 0 ){
          directions[count]=2;
          count++;    
        }
        //down
        if ( visitmatrix[xgen+1][ygen] == 0 ){
          directions[count]=4;
          count++;    
        }
        //left
        if ( visitmatrix[xgen][ygen-1] == 0 ){
          directions[count]=8;
          count++;    
        }  
        if (showmaze == 1 ){ 
          strip.setPixelColor(displaymatrix[xgen-1][ygen-1], 32, 32, 32);

          strip.show();
          delay(50);
        }
        // no dir found
        if (count == 0 ) {

if (digitalRead(popendsw) == 0 ){
  currentpop = rowStack.count();
  if (currentpop > countpop ) {
    countpop = currentpop;
  xend = xgen-1;
  yend = ygen-1;
  Serial.print("longest pop : ");
  Serial.println(currentpop);
  }
}

         // mled.dot(x-1,y-1);
         // mled.display();
          xgen = rowStack.pop();
          ygen = colStack.pop();

         // Serial.println("popping ");
          } else {
          // count random direction
          int dir = directions[random(count)];
          //Serial.println("push ");
          rowStack.push(xgen); 
          colStack.push(ygen);
         // Serial.print("nr dir : "); 
         // Serial.println(count);
          //delay(100);
         // Serial.println(dir);
          // move 1,1 to 0,0
          //mled.dot(x-1,y-1);
          //mled.display();
          // set direction in maze, dit moet bit set worden
          int mybits = maze[xgen-1][ygen-1];
          int storedir = mybits | dir;
          maze[xgen-1][ygen-1] = storedir;
          if ( dir == 1){
          int getup = maze[xgen-2][ygen-1];
          int storedir = getup | 4;
          maze[xgen-2][ygen-1] = storedir;
          }
          if ( dir == 2){
          int getup = maze[xgen-1][ygen];
          int storedir = getup | 8;
          maze[xgen-1][ygen] = storedir;
          }
          if ( dir == 4){
          int getup = maze[xgen][ygen-1];
          int storedir = getup | 1;
          maze[xgen][ygen-1] = storedir;
          }
          if ( dir == 8){
          int getup = maze[xgen-1][ygen-2];
          int storedir = getup | 2;
          maze[xgen-1][ygen-2] = storedir;
          }


          
        //  maze[x-1][y-1] = dir;
          //set new square
          if (dir == 1){ xgen--; }
          if (dir == 2){ ygen++; }
          if (dir == 4){ xgen++; }
          if (dir == 8){ ygen--; }
          visitmatrix[xgen][ygen]=1;
          //drawmaze();
          }
        }
        notalldone = 0;                                                  //#2
        // if found 0 in 10x10 matrix visited, do
        for(int checkx=0;checkx<10;checkx++){
          for(int checky=0;checky<10;checky++){
            if ( visitmatrix[checkx][checky] == 0 ){
              tmpx=xgen;
              tmpy=ygen;
              notalldone = 1;  
            }
          }
        }
   }
rowStack.push(tmpx); 
colStack.push(tmpy);

}




void setup() {
  pinMode(32, INPUT_PULLUP);
  pinMode(33, INPUT_PULLUP);
  pinMode(25, INPUT_PULLUP);
  pinMode(26, INPUT_PULLUP);
  pinMode(popendsw, INPUT_PULLUP);
  pinMode(trailsw, INPUT_PULLUP);
  pinMode(restartsw, INPUT_PULLUP);
  pinMode(showmazesw, INPUT_PULLUP);
  
  Serial.begin(115200);

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip.setBrightness(10);

 unsigned long seed = 0;
for (int i=0; i<32; i++)
{
  seed = seed | ((analogRead(A0) & 0x01) << i);
}
randomSeed(seed);

if (digitalRead(showmazesw) == 0){
  showmaze = 1;
}


mazegen();
drawmaze();
drawmaze2();
 
 strip.fill(0, 0, 64);
  strip.show(); // Initialize all pixels to 'off'
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.setPixelColor(displaymatrix[xend][yend], 0, 255, 0);

  strip.show();

  x=0;
  y=0;
  
}

uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}


void reset2start() {
      strip.setPixelColor(displaymatrix[x][y], 0, 0, 0);
    strip.show();
  x = 0;
  y = 0;
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip.setBrightness(10);
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.setPixelColor(displaymatrix[xend][yend], 0, 255, 0);
  strip.show();

}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);

  }
}

void loop() {


if (digitalRead(trailsw) == 0 ){
  trail = 32;
} else { trail = 0; }

if (digitalRead(restartsw) == 0 ){
  mode = 2;
} else {
  mode = 0;
}
    int isUp = (bitRead(maze[x][y], 1));
    int isRight = (bitRead(maze[x][y], 2));
    int isDown = (bitRead(maze[x][y], 3));
    int isLeft = (bitRead(maze[x][y], 0));
  
//isUp = 1;
//isDown = 1;
//isLeft = 1;
//isRight = 1;

if (digitalRead(up) == 0) {
   
  if (isUp == 1){

  strip.setPixelColor(displaymatrix[x][y], 0, 0, trail);
  y++;
     drawmaze();
  if ( y > 7) { y=7;}
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.show();
  } else {
    strip.setPixelColor(displaymatrix[x][y], 255, 0, 0);
    strip.show();
    delay(100);
    strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
    strip.show();
    if (mode == 2){ 
      delay(1000);
      reset2start();
    }
  }
}
if (digitalRead(down) == 0) {
  if (isDown == 1){
   
  
  strip.setPixelColor(displaymatrix[x][y], 0, 0, trail);
  y--;
       drawmaze();

  if ( y < 0) { y=0;}
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.show();
  } else {
    strip.setPixelColor(displaymatrix[x][y], 255, 0, 0);
    strip.show();
    delay(100);
    strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
    strip.show();
    if (mode == 2){ 
      delay(1000);
      reset2start();
    }
  }
}
if (digitalRead(left) == 0) {
      drawmaze();
  if (isLeft == 1){
  strip.setPixelColor(displaymatrix[x][y], 0, 0, trail);
  x--;
       drawmaze();

  if ( x < 0) { x=0;}
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.show();
  } else {
    strip.setPixelColor(displaymatrix[x][y], 255, 0, 0);
    strip.show();
        delay(100);
    strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
    strip.show();
    if (mode == 2){ 
      delay(1000);
      reset2start();
    }
  }
}
if (digitalRead(right) == 0) {
      drawmaze();
  if (isRight == 1){
  strip.setPixelColor(displaymatrix[x][y], 0, 0, trail);
  x++;
       drawmaze();

  if ( x > 7) { x=7;}
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.show();
  } else {
    strip.setPixelColor(displaymatrix[x][y], 255, 0, 0);
    strip.show();
    delay(100);
    strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
    strip.show();
    if (mode == 2){ 
      delay(1000);
      reset2start();
    }
  }
}
if (x ==xend && y == yend){
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  rainbow(20);
}

delay(200);

}

Visual Code and local ssh based git

I’ve gone back and front using Visual Code.
For work and now with platformio.
(I’ve been using platformio on the commandline only, until recently.)

While i have some private github/gitlab accounts, I alway had some local git repositories.
Using in the past GiTea, and a ssh based one.

Below: This own hosted SSH setup allows me to use Visual Code to use as git repository

========== On you ssh server

adduser git
su - git
mkdir .ssh && chmod 700 .ssh
touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys
chsh git -s $(which git-shell)
# Note .. there is no interactive shell! 

== locate git-shell-commands 
locate git-shell-commands
cp -r /usr/share/doc/git/contrib/git-shell-commands /home/git/
chmod +x /home/git/git-shell-commands/*

== Make sure git-shell is in /etc/shells

== Make a git repo destination

mkdir -p /mygitstuff/git
cd /mygitstuff/git
mkdir project.git
cd project.git
git init --bare

========== On your workstation

== copy ssh public key in .ssh/authorized_keys on the server

cd myproject
git init
git add .
git commit -m 'Initial commit'
git remote add origin git@gitserver:/mygitstuff/git/project.git
git push origin master

Backtracking maze generation with Arduino

As POC for the maze game.

Maze generation!

Make an initial cell the current cell
   mark it as visited
While there are unvisited cells
   If the current cell has any neighbours
   which have not been visited
         Choose randomly one of the unvisited neighbours
                Push the current cell to the stack
                Mark wall hole
                Make the chosen cell the current cell
                mark it as visited
        Else if stack is not empty
                Pop a cell from the stack
                Make it the current cell

This is my implementation of backtracking

The displaymatrix function is a implementation of different led mappings

Still have to decide where to place endpoint …
At 8,8 or at first stack pop?
Maybe both?

Code

#include <WEMOS_Matrix_LED.h>
#include <StackArray.h>

int directions[4]{};
int notalldone = 1;
int tmpx=0;
int tmpy=0;
int x = 1;
int y = 1;
MLED mled(5); //set intensity=5

int maze[8][8] = { 
  };

int displaymatrix[8][8] = { 
{ 0,1,2,3,4,5,6,7 },
{ 8,9,10,11,12,13,14,15 }, 
{16,17,18,19,20,21,22,23},
{24,25,26,27,28,29,30,31},
{32,33,34,35,36,37,38,39},
{40,41,42,43,44,45,46,47},
{48,49,50,51,52,53,54,55},
{56,57,58,59,60,61,62,63}
};

int visitmatrix[10][10] = { 
  1,1,1,1,1,1,1,1,1,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,1
  };

void setup() {
    Serial.begin(115200);
    randomSeed(analogRead(0));
    mazegen();
    drawmaze();
}

void mazegen(){
  visitmatrix[x][y]=1;       
  StackArray <int> rowStack; 
  StackArray <int> colStack;
        rowStack.push(x);
        colStack.push(y);
  while(notalldone == 1){    
     visitmatrix[x][y]=1;
        while(!rowStack.isEmpty()) {
        int count=0;
        //up
        if ( visitmatrix[x-1][y] == 0 ){
          directions[count]=1;
          count++;    
        }
        //right
        if ( visitmatrix[x][y+1] == 0 ){
          directions[count]=2;
          count++;    
        }
        //down
        if ( visitmatrix[x+1][y] == 0 ){
          directions[count]=4;
          count++;    
        }
        //left
        if ( visitmatrix[x][y-1] == 0 ){
          directions[count]=8;
          count++;    
        }  
        // no dir found
        if (count == 0 ) {

          mled.dot(x-1,y-1);
          mled.display();
          x = rowStack.pop();
          y = colStack.pop();

          Serial.println("popping ");
          } else {
          // count random direction
          int dir = directions[random(count)];
          Serial.println("push ");
          rowStack.push(x); 
          colStack.push(y);
          Serial.print("nr dir : "); 
          Serial.println(count);
          //delay(100);
          Serial.println(dir);
          // move 1,1 to 0,0
          mled.dot(x-1,y-1);
          mled.display();
          // set direction in maze, dit moet bit set worden
          int mybits = maze[x-1][y-1];
          int storedir = mybits | dir;
          maze[x-1][y-1] = storedir;
          if ( dir == 1){
          int getup = maze[x-2][y-1];
          int storedir = getup | 4;
          maze[x-2][y-1] = storedir;
          }
          if ( dir == 2){
          int getup = maze[x-1][y];
          int storedir = getup | 8;
          maze[x-1][y] = storedir;
          }
          if ( dir == 4){
          int getup = maze[x][y-1];
          int storedir = getup | 1;
          maze[x][y-1] = storedir;
          }
          if ( dir == 8){
          int getup = maze[x-1][y-2];
          int storedir = getup | 2;
          maze[x-1][y-2] = storedir;
          }


          
        //  maze[x-1][y-1] = dir;
          //set new square
          if (dir == 1){ x--; }
          if (dir == 2){ y++; }
          if (dir == 4){ x++; }
          if (dir == 8){ y--; }
          visitmatrix[x][y]=1;
          drawmaze();
          }
        }
        notalldone = 0;                                                  //#2
        // if found 0 in 10x10 matrix visited, do
        for(int checkx=0;checkx<10;checkx++){
          for(int checky=0;checky<10;checky++){
            if ( visitmatrix[checkx][checky] == 0 ){
              tmpx=x;
              tmpy=y;
              notalldone = 1;  
            }
          }
        }
   }
rowStack.push(tmpx); 
colStack.push(tmpy);
}



void drawmaze(){
  Serial.println("Generating done - Drawing");
  for(int ledx=0;ledx<8;ledx++)
  {
    for(int ledy=0;ledy<8;ledy++){
    Serial.print(maze[ledx][ledy]);  
    if ( maze[ledx][ledy] != 0 ) {
        mled.dot(ledx,ledy); // draw dot
        mled.display();
       // delay(50);
    }
      }  
    Serial.println("");
  }
  Serial.println("");
  delay(100);
}


void loop() {
}

Simple FreeOTP access for PHP

I’ve played with FreeOTP for my own websites in the past, but here is a cleaned up version.

apt-get install qrencode oathtool

Using a webserver with php you can use below simple sniplet to restrict access.

<?PHP
$output = shell_exec('oathtool --totp=sha256 --base32 ################PLACE-SECRET-OUTPUT-HERE-############');
$otp=$_POST["otp"];
$stripped = preg_replace('/\D/', '', $output);
$strippedotp = preg_replace('/\D/', '', $otp);
?>
<form action="" method="post">
OTP: <input type="text" name="otp"><br>
<input type="submit">
</form>
<?PHP
if(strcmp("$strippedotp", "$stripped") == 0)
{ echo "Access"; }
else
{ echo "No Access"; }
?>

bash script to generate secret and qrcode

#!/bin/bash
secret=$(echo 1234567812345678 | base32)
echo "Secret : (place in PHP file)"
echo $secret
qrencode -t ANSI256 -o - $(echo otpauth://totp/Mine:myname?secret=${secret}\&issuer=Mine\&algorithm=SHA256\&digits=6\&period=30) -o qrcode.png -t png -s 5

Above generates a QRcode you can import in FreeOTP

Mikrotik and Zerotier

Today i’ve upgraded one of my Mikrotiks to 7.8.

And installed the zerotier extra package!
I’m not going to explain what zerotier is .. read it here:
https://www.zerotier.com/

I already did some tests using my phone and a linux server.

  1. Upgrade to the latest stable version (at this time : 7.8)
  2. Install from the extra packages zerotier-7.8.arm.npk
    upload to the files folder and reboot your MT

Add zerotier network

Allow connection in the Zerotier Gui

Mikrotik changes from ACCESS_DENIED to OK

Ping test from MT to my Phone !

Maybe we could setup a adhoc zerotier network with some of my friends to do some remote assisting when needed!

Little Led Matrix Maze game

While doing stuff like, making our home a little greener. Smoking meat. Working on diorama’s and my Escape game. I found time to make this little maze game.

Using an ESP32, mini joystick and a 8×8 led matrix. The objective is to get to the other side of the invisible maze.

It is a blind maze, so you have to figure out the path by trail and error. I found it quite fun and entertaining. (Coline had a hard time finishing the mode 3 maze)

I’ve got 3 settings on the maze:
0 – There is a trail where you have been.
1 – No trail, but only red leds showing walls.
2 – No trail, red reds and a reset to square 0,0 .. so you have to remember the path you previously took.

I’ll add code and schematics tomorrow …

Light blue shows you where you have been

Mode 2 game, reset when hitting a wall

Hitting the end block!

Maze is static at the moment, i’m planning to implement a “Recursive division method” to generate the maze.

Code

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>

// joystick pins
int up=33;
int down=25;
int left=32;
int right=26;
int cursor=32;

// 0 easy = trail // 1 only red walls // 2 = reset to 0.0
int mode=2;

//int trail=32;
int trail=0;

// Which pin on the Arduino is connected to the NeoPixels?
#define LED_PIN    2

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 64

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

// bits set opening in square
//    2
//   -----
// 1 |   | 4
//   -----
//     0
// so 5 is a passage from left to right (1+4) 

int maze[8][8] = {
  4,5,3,6,5,5,5,3,
  6,5,11,12,5,3,6,9,
  14,1,12,5,3,10,12,1,
  12,5,5,3,10,12,5,3,
  2,6,5,9,14,5,1,10,
  10,10,6,5,9,6,5,9,
  12,11,10,6,1,10,6,1,
  4,9,12,13,5,13,13,1,
  };

int displaymatrix[8][8] = { 
{ 0,1,2,3,4,5,6,7 },
{ 15,14,13,12,11,10,9,8 }, 
{16,17,18,19,20,21,22,23},
{31,30,29,28,27,26,25,24},
{32,33,34,35,36,37,38,39},
{47,46,45,44,43,42,41,40},
{48,49,50,51,52,53,54,55},
{63,62,61,60,59,58,57,56}
};

int x = 0;
int y = 0;

void setup() {
// joy
  pinMode(32, INPUT_PULLUP);
  pinMode(33, INPUT_PULLUP);
  pinMode(25, INPUT_PULLUP);
  pinMode(26, INPUT_PULLUP);

// mode set with jumpers
  pinMode(34, INPUT_PULLUP);
  pinMode(35, INPUT_PULLUP);

  Serial.begin(115200);

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip.setBrightness(10);
// set begin and end pixel
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.setPixelColor(displaymatrix[7][7], 0, 255, 0);

  strip.show();
//mode select  
 if (digitalRead(34) == 0) {
 mode=0;
 if (digitalRead(35) == 0) {
 mode=2;
 } else { 
 mode=1; 
 }
// finish effect
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

// reset to start (mode 2)
void reset2start() {
      strip.setPixelColor(displaymatrix[x][y], 0, 0, 0);
    strip.show();
  x = 0;
  y = 0;
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip.setBrightness(10);
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.setPixelColor(displaymatrix[7][7], 0, 255, 0);
  strip.show();

}
// finish effect
void rainbow(uint8_t wait) {
  uint16_t i, j;
  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

void loop() {
    int isUp = (bitRead(maze[x][y], 1));
    int isRight = (bitRead(maze[x][y], 2));
    int isDown = (bitRead(maze[x][y], 3));
    int isLeft = (bitRead(maze[x][y], 0));
if (digitalRead(up) == 0) {
  if (isUp == 1){
  strip.setPixelColor(displaymatrix[x][y], 0, 0, trail);
  x++;
  if ( x > 7) { x=7;}
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.show();
  } else {
    strip.setPixelColor(displaymatrix[x][y], 255, 0, 0);
    strip.show();

    if (mode == 2){ 
      delay(1000);
      reset2start();
    }
  }
}
if (digitalRead(down) == 0) {
  if (isDown == 1){
  strip.setPixelColor(displaymatrix[x][y], 0, 0, trail);
  x--;
  if ( x < 0) { x=0;}
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.show();
  } else {
    strip.setPixelColor(displaymatrix[x][y], 255, 0, 0);
    strip.show();

    if (mode == 2){ 
      delay(1000);
      reset2start();
    }
  }
}
if (digitalRead(left) == 0) {
  if (isLeft == 1){
  strip.setPixelColor(displaymatrix[x][y], 0, 0, trail);
  y--;
  if ( y < 0) { y=0;}
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.show();
  } else {
    strip.setPixelColor(displaymatrix[x][y], 255, 0, 0);
    strip.show();
    if (mode == 2){ 
      delay(1000);
      reset2start();
    }
  }
}
if (digitalRead(right) == 0) {
  if (isRight == 1){
  strip.setPixelColor(displaymatrix[x][y], 0, 0, trail);
  y++;
  if ( y > 7) { y=7;}
  strip.setPixelColor(displaymatrix[x][y], 0, 0, 255);
  strip.show();
  } else {
    strip.setPixelColor(displaymatrix[x][y], 255, 0, 0);
    strip.show();

    if (mode == 2){ 
      delay(1000);
      reset2start();
    }
  }
}
if (x ==7 && y == 7){
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  rainbow(20);
}
delay(200);

}

Quotes and slips of the tongue

Some cheesy stuff I found on my fileserver.
Mine/Friends or heared in the wild.

Hey kaas met ketchup? Of rasp ik nu te ver? (dutch)
(cheese with ketchup? Or am I grating too far?)

Coline in chat: I’m planning to go to sleep, but i was mentally ordering things in my head
My reply: cat /dev/head | sort -n > /dev/null && sleep $((8 * 2600))

Russisch routeren (dutch)
(Russian Routing) (Network joke about misconfiguring a router – like roulette)

Dashboard of my room kamer (windowsill)

Howmuch is that in beers?

me: I’ve seen better code
me also: But not written by me

Due to the recent coffee panic here, i think i need to prepare for the worst.. and go get a UPS for my backup coffee machine. (and bury cans of coffee in the garden)

Have you you seen the papers yet? Or are you waiting for the movie?

There are 1/12 problems according to the program
No problem at leas not 1 out of 0 !

We have no fruit left, only a sick banana

Take it easy, take it easy.
You can break things also slowly

Windows 95 – A world is closing for you.

Kapot gechmod (dutch and it rhymes)
Break something using linux chmod command

I don’t have a smart TV, I need to be smart myself

A beautiful mind is about a misunderstood genius
Happens to me also sometimes

Worldwide standard .. in Enschede

That dude was so drunk, he spoke encrypted

How long a drive was it?
About 1 liter

Will you rewind the CD after using it please?

Better to have 10 beers in you, than 1 on the street

if microsoft is the answer you didn’t understand the question

The release date of windows 2000 will be delayed, the new releasedate will be 14-04-1901

That whisky bottle is square!
Nice, no wet circles on the table

That trashcan bag is too big!
Maybe it is supposed to be on the outside

Sparkling water = water with holes

Many orgasms in the water .. organism

Sollution for everything, apply some glue and some pressure

I have to go, I have to catch the bike in 5 minutes

I heared some fireworks, mid year.
And said “maybe they are marsians”, we have to check if they lighting fireworks over 687 days again.

Finished a display case for the SDK-85

So i made a nice display case for this SDK.

I made some designs and came up with this:

I just took a 12mm x 60mm piece of wood, and made a slit for the acrylic plastic using a circular saw, and removed a part for the back-plate using a wood router. I found a piece of acrylic in my shed, cut it to the right size using the circular saw. Slapped some paint on the wood.

I want to be able to use the sdk whenever i want, so i made a sliding window thingy. ( With wooden handle so the frame looks intact when closed. )

Then I 3D printed some holders, which I designed using Openscad.

Openscad code

difference(){
difference(){

    translate([0,8,1])
    cube([20,15,10],center=true);
union(){
    cylinder(h=9, d1=9, d2=9, center=true);
translate([0,0,3])
cylinder(h=9, d1=15, d2=15, center=true);
}
   
}
$fn=100;
 translate([0,12,0])
 cylinder(h=30, d1=3.5, d2=3.5, center=true);
}

I will probably add a powerconnector and lasercut a hole for the keypad at a later stage.

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