tech explorers, welcome!

Tag: model

DFRobot case for UNIHIKER

The small and efficient form factor of the UNIHIKER makes it really easy to craft a case for it.

For my smart home asssistant I was looking for an android-like style, and the DFRobot logo is perfect for the UNIHIKER, making tribute to their developers.

Github Repo

I've released a github repository where I will be open-sourcing all the model files and people can contribute with their own, so feel free to create a pull request and share your designs!

https://github.com/TheRoam/DFRobot-UNIHIKER-case/

It includes a github page where models can be previewed:

https://theroam.github.io/DFRobot-UNIHIKER-case/

Unihiker_DFRcase_v1

This is my first release, used for testing and including all the basic features for my home assistant.

Files

https://github.com/TheRoam/DFRobot-UNIHIKER-case/tree/main/blender

Features

  • Top openings for text display through touch screen.
  • Side opening for USB-C connection.
  • Back opening for external sensor cabling.
  • Back extrusions for 40mm speaker placement.
  • Foot-like support for vertical standing.

Case parts

  1. Bottom piece acts as a casing.
  2. Internal support piece holds UNIHIKER board to the bottom piece using the screws on the board.
  3. Top piece acts as a cover and clips on bottom piece.
  4. Feet support enables vertical standing of the case.
  5. Antennas just to match the deisgn of the DFRobot logo.

Assembly

  1. Place the screws of the internal support piece and screw it to the UNIHIKER
  1. Place the UNIHIKER inside the bottom piece. If you're using external sensors, you can bring your cabling outside using the void at the back.
  1. Hold the board to the case using a pair of 2.5mm screws from the back of the bottom piece.
  1. Fit the top piece in place as it should just hold itself.
  1. Place the feet support and the antennas in place. You can glue these to make sure that they stay in place.

And that's your case crafted with a nice DFRobot android look!

Share your thoughts on Twitter!

🐦 @RoamingWorkshop

Three.js: a 3D model viewer for your site

I was writing a post where I wanted to insert a 3D model to picture it better, and I even thought in doing a viewer myself. But I didn’t have to browse long to find Three.js.

https://threejs.org/

It’s all invented!

For this example, I'll use the official CDN libraries, instead of downloading the files to a server.

Create a basic .html file as suggested in the documentation:

https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body { margin: 0; }
		</style>
	</head>
	<body>
        <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

        <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/[email protected]/build/three.module.js"
            }
          }
        </script>

        <script>
        //App code goes here
        </script>
	</body>
</html>

Create a scene

Let's keep with the example and fill up the second <script> block defining a scene with an animated rotating cube:

<script type="module">
        import * as THREE from 'three';
	const scene = new THREE.Scene();
	const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

	const renderer = new THREE.WebGLRenderer();
	renderer.setSize( window.innerWidth, window.innerHeight );
	document.body.appendChild( renderer.domElement );

	const geometry = new THREE.BoxGeometry( 1, 1, 1 );
	const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
	const cube = new THREE.Mesh( geometry, material );
	scene.add( cube );

	camera.position.z = 5;

	function animate() {
		requestAnimationFrame( animate );

		cube.rotation.x += 0.01;
		cube.rotation.y += 0.01;

		renderer.render( scene, camera );
	};

	animate();
</script>

All of this would look like this:

Add drag controls and background

Now we have a base to work with. We can add some functionality inserting the OrbitControls module.

//Import new modules at the beginning of the script
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';

//then add the mouse controls after declaring the camera and renderer
const controls = new OrbitControls( camera, renderer.domElement );

Also, you can modify the background easily, but you will need to host your images within your app in a server, or run it locally, because of CORS. I will be using the background image of the blog header, which was taken from Stellarium.

First define a texture. Then, add it to the scene:

//do this before rendering, while defining the scene
//define texture
const texture = new THREE.TextureLoader().load( "https://theroamingworkshop.cloud/demos/Unity1-north.png" );

//add texture to scene
scene.background=texture;

Full code:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body { margin: 0; }
		</style>
	</head>
	<body>
        <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

        <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/[email protected]/build/three.module.js"
            }
          }
        </script>
<body style="margin: 0; width:100%;height:300px;">
        <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

        <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/[email protected]/build/three.module.js"
            }
          }
        </script>

    <script type="module">

    import * as THREE from 'three';
	import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';

	const scene = new THREE.Scene();
    
	const texture = new THREE.TextureLoader().load( "https://theroamingworkshop.cloud/demos/Unity1-north.png" );
	scene.background=texture;

    const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

	const renderer = new THREE.WebGLRenderer();
	renderer.setSize( window.innerWidth, window.innerHeight );
	document.body.appendChild( renderer.domElement );
    
    const controls = new OrbitControls( camera, renderer.domElement );

	const geometry = new THREE.BoxGeometry( 1, 1, 1 );
	const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
	const cube = new THREE.Mesh( geometry, material );
	scene.add( cube );

	camera.position.z = 5;

	function animate() {
		requestAnimationFrame( animate );

		cube.rotation.x += 0.01;
		cube.rotation.y += 0.01;

		renderer.render( scene, camera );
	};

	animate();
</script>
</body>
</html>

Insert a 3D model

Now let's replace the cube for our own 3D model which, in the case of Three.js, will be a glTF (.GLB o .GLTF) format, that is most supported and renders faster (.fbx, .stl, .obj and so on are also supported).

I will export a .glb of this basic Raspberry Pi 4B case that I did some time ago using Blender:

Now, replace the <script> block based on the "webgl_loader_gltf" which was shown at the start of the post:

<script type="module">
import * as THREE from 'three';
import { GLTFLoader } from 'https://unpkg.com/[email protected]/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';

let camera, scene, renderer;

init();
render();

function init() {

	const container = document.createElement( 'div' );
	document.body.appendChild( container );

	camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.1, 20 );
    camera.position.set( 0.2, 0.2, 0.2 );

	scene = new THREE.Scene();        
    scene.add( new THREE.AmbientLight( 0xffffff, 0.75 ) );

	const dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
	dirLight.position.set( 5, 10, 7.5 );
	dirLight.castShadow = true;
	dirLight.shadow.camera.right = 2;
	dirLight.shadow.camera.left = - 2;
	dirLight.shadow.camera.top	= 2;
	dirLight.shadow.camera.bottom = - 2;
	dirLight.shadow.mapSize.width = 1024;
	dirLight.shadow.mapSize.height = 1024;
	scene.add( dirLight );

    //model
     const loader = new GLTFLoader();
	 loader.load( 'https://theroamingworkshop.cloud/threeJS/models/rPi4case/rPi4_case_v1.glb', function ( gltf ) {
		scene.add( gltf.scene );
		render();
	 } );

	renderer = new THREE.WebGLRenderer( { antialias: true } );
            
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.toneMapping = THREE.ACESFilmicToneMapping;
	renderer.toneMappingExposure = 1;
	renderer.outputEncoding = THREE.sRGBEncoding;
	container.appendChild( renderer.domElement );

	const controls = new OrbitControls( camera, renderer.domElement );
	controls.addEventListener( 'change', render );
    controls.minDistance = 0.001;
	controls.maxDistance = 1;
	controls.target.set( 0.03, 0.01, -0.01 );
	controls.update();
	window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( window.innerWidth, window.innerHeight );
	render();
}
function render() {
	renderer.render( scene, camera );
}
</script>

Basically it does the following:

  • Import used modules:
    • GLTFLoader will load our model in .glb format
    • OrbitControls for camera rotation and position
  • Define the scene:
    • define a camera
    • define the light (in this case, ambient and directional; try commenting each of them to see the difference)
    • define a background
  • Load the model in the scene
  • Define rendering parameter and render.

All of it finally looks like this (click and drag!):

Hope it's useful! Doubts or comments on 🐦 Twitter!

🐦 @RoamingWorkshop