Posted on

Boeing Automated Honeycomb Repair System | Cal State Los Angeles

The requirements for an Automated Honeycomb Repair System was set forth by Boeing’s Defense, Space and Security division, where honeycomb panels are heavily used on satellite structures in space, government, and military communication applications. This was a joint senior project by students at Cal State Los Angeles and The Boeing Company.

The requirements were to design an automated repair system which would repair dents and damages on honeycomb panels in an automated fashion such that it would speed up the current “manual repair” of honeycomb panels which currently take an approximated 3-4 hours to repair a single dent.

The basic concept of the process is to drill out the top face-sheet of the honeycomb with a 1/4″ drill, core pick an inner diameter of 1″ under the drilled top face-sheet to the bottom face-sheet, vacuum debris, and finally foam fill the core-picked void / affected area.

To achieve the aforementioned process, the mechanical sub-system consists of a ball-screw driven Y and Z axis and a rotary A axis for performing a tool change turret style and which also makes angled entry of a custom core-picker tool possible, which was also custom designed by our team for the actual core-picking task.

The electrical sub-system consists of a single Arduino Due for complete machine control and automation driving: 3 Stepper Motors & Drives, a Servo Motor + Drive, 6 Solenoid Valves, Full 20×4 LCD Display for Status updates throughout the process, Full Color RGBW LED Strip for visual status feedback, 8 Relays, also Input devices include: 4 optical homing sensors, one for each axis, tool touch plate input, probe input, and finally start + stop inputs.

All possible noise generating devices such as power supplies and motor drives were placed far away from logic level devices such as the control microprocessor / Arduino. This was initially designed with that in mind as can be seen in the Solid-works model layout.

From Solid-works model on the left to realization on the right, the figure above shows the actual machine after fabrication. The entire machine automation process brought down the manual repair time of 4-6 hours to roughly 10 minutes.

Below is a time-lapse of the entire process of the Automated Repair Machine Process.

The video begins with all the user-replaceable tools being “tool-touched” or measured for any change due to wear or replacement, as they will not be exactly placed into location via human hands, this applies to the drill bit, probe, and custom core-picking tool. Next the face of the honeycomb panel is measured via a probe, and drilling of the face sheet is performed. After drilling, the bottom face-sheet is measured to make sure the next process of core picking does not puncture the bottom face sheet when clearing the inner honeycomb core. Using those measured distances from top to bottom face-sheet, the core picking process is performed, pay attention to the fine motor movements required for the custom angled tool to make entry and exit the previously drilled hole. Next the vacuum clears all the debris in the core picked void space, and finally foam is filled into the affected area, completing the repair process, the machine waits for another dent and start button press to begin its process again.

Senior Team Members: Allen Analian (EE), Jeffrey T. Iwasaki (ME), Jonathan Sanabria (ME), Gino Kiettisak (ME), Carlos A. Sanchez (ME), Alberto M. Cortes (EE)

Acknowledgments: Special Thanks to the Boeing Company & Cal State Los Angeles for the opportunity, Theodore (Ted) Nye (Senior Design Director), Teofilo El-Masri, Cameron Massey, Jose Armenta, Jonathan Fish (Boeing Liaisons).

Posted on

ESP8266 NodeMCU – Toggle Button & Slider – Remote Node.js Server Example

After looking all over for bits and pieces of how to properly get a Remote Node.js Server to communicate with a ESP8266 or ESP32 NodeMCU, I was finally able to get everything working properly. With so many libraries of the same variant of implementing a Socket.io client on a NodeMCU, it was very confusing when functions had the same or very similar names and arguments.

I wanted a way to remotely control a device with a button toggle switch (LED, Mosfet, Relay etc) and a range slider to control brightness of an LED, Triac etc with a web based interface on a NodeMCU, communicating to a centralized web server.

Here is an example of how I was able to achieve it with fairly great results. Hopefully this helps others in trying to achieve the same results. For more buttons and sliders their values can be stored in 2 arrays one for switch state values and another for dimmer pwm values, both with a corresponding id to each button or slider.

Below is the Server.js file running on the remote Node.js Web Server :


var http = require('http');

var express = require('express');

var path = require('path');

var app = express();

// all environments
app.set('port', process.env.PORT || 3000);

app.get('/', function (req, res) {

res.sendFile(__dirname + '/index.html');

});
app.use(express.static(__dirname + '/'));

var server = http.createServer(app);

var io = require('socket.io').listen(server);

server.listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});

// Define/initialize our global vars
var socketCount = 0

var brightness = 0; //static variable to hold the current brightness
var swstate = 0;

io.sockets.on('connection', function(socket){
//console.log(socket.id);
// Socket has connected, increase socket count
socketCount++
// Let all sockets know how many are connected
io.sockets.emit('users connected', socketCount)

socket.emit('swstate', swstate);

socket.on('swstate',function(){
swstate = 1 - swstate;
//console.log(swstate);
io.emit('swstate', swstate);
});

socket.emit('led', {value: brightness}); //send the new client the current brightness

socket.on('led', function (data) { //makes the socket react to 'led' packets by calling this function
brightness = data.value; //updates brightness from the data object
io.sockets.emit('led', {value: brightness}); //sends the updated brightness to all connected clients
});

});

process.on('SIGINT', function() {
//Do things on Script Termination...
console.log('\n Bye!')
process.exit();
});

Start the node.js server script on the remote server using the command in SSH:
forever start -o out.log -e err.log Server.js

Below is the index.html file running in the same directory as the Node.js Script (This page is served by the Node.js http Server) :

<html>
<head>
<title>Node.js Socket.io Testing</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/css/bootstrap2/bootstrap-switch.min.css">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fjquery%2F1.11.3%2Fjquery.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fcdnjs.cloudflare.com%2Fajax%2Flibs%2Frangeslider.js%2F2.3.2%2Frangeslider.min.css%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
<!--[if IE]>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fcdnjs.cloudflare.com%2Fajax%2Flibs%2Fjson3%2F3.3.2%2Fjson3.min.js%22%3E%0A%3Cscript%20src%3D%22https%3A%2F%2Fcdnjs.cloudflare.com%2Fajax%2Flibs%2Fsocket.io%2F2.1.1%2Fsocket.io.dev.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
<![endif]-->
<!--[if !IE]><!-->
<!--<![endif]-->
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fsocket.io%2Fsocket.io.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fmaxcdn.bootstrapcdn.com%2Fbootstrap%2F3.3.5%2Fjs%2Fbootstrap.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fcdnjs.cloudflare.com%2Fajax%2Flibs%2Fbootstrap-switch%2F3.3.2%2Fjs%2Fbootstrap-switch.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fgitcdn.github.io%2Fbootstrap-toggle%2F2.2.2%2Fjs%2Fbootstrap-toggle.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fcdnjs.cloudflare.com%2Fajax%2Flibs%2Frangeslider.js%2F2.3.2%2Frangeslider.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
 
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0Abody%20%7B%0Amargin-top%3A%2020px%3B%0Amargin-left%3A%2030px%3B%0A%7D%0A%0Ainput%5Btype%3Drange%5D%7B%0A-webkit-appearance%3A%20none%3B%0Awidth%3A%2080%25%3B%0A%7D%0A%0Ainput%5Btype%3Drange%5D%3A%3A-webkit-slider-runnable-track%20%7B%0Aheight%3A%2010px%3B%0Abackground%3A%20%230c75b3%3B%0Aborder%3A%20none%3B%0Aborder-radius%3A%203px%3B%0A%7D%0A%0Ainput%5Btype%3Drange%5D%3A%3A-webkit-slider-thumb%20%7B%0A-webkit-appearance%3A%20none%3B%0Aborder%3A%20none%3B%0Aheight%3A%2032px%3B%0Awidth%3A%2032px%3B%0Aborder-radius%3A%2050%25%3B%0Abackground%3A%20%2314c0dc%3B%0Amargin-top%3A%20-12px%3B%0A%7D%0A%0Ainput%5Btype%3Drange%5D%3Afocus%20%7B%0Aoutline%3A%20none%3B%0A%7D%0A%0Ainput%5Btype%3Drange%5D%3Afocus%3A%3A-webkit-slider-runnable-track%20%7B%0Abackground%3A%20%230c75b3%3B%0A%7D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="
<style>" title="
<style>" />

</head>
<body>
 

<div class="row">
 

<div class="alert alert-info col-md-3 col-md-3" role="alert" id="usersConnected"></div>

 
</div>

 
<strong>Slider Value: </strong><span id="outputText">50</span>
 
<input type="range" id= "inputSlider" min="0" max="1023" value="0" step="1" oninput="showValue(this.value)" />
 
<strong>SW State: </strong><span id="swval"></span>
 
<input type="checkbox" data-toggle="toggle" data-on="Off" data-off="On" id="toggle">
 
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%0A%2F%2F%20Connect%20to%20our%20node%2Fwebsockets%20server%0Avar%20socket%20%3D%20io.connect('http%3A%2F%2Fwww.domain.com%3A3000')%3B%0A%0Avar%20websocket%20%3D%200%3B%0A%0A%24(%22%23toggle%22).change(function()%7B%0Aif(!websocket)%20%7B%0Aif(%24(%22%23toggle%22).prop(%22checked%22)%20%3D%3D%201)%7B%0Avar%20swstate%20%3D%201%3B%0Asocket.emit('swstate'%2C%20swstate)%3B%0A%7Delse%7B%0Avar%20swstate%20%3D%200%3B%0Asocket.emit('swstate'%2C%20swstate)%3B%0A%7D%0A%7D%20else%20%7Bwebsocket%20%3D%200%3B%7D%0A%7D)%3B%0A%0Asocket.on('swstate'%2C%20function(swstate)%20%7B%0AswValue(swstate)%3B%0Awebsocket%20%3D%201%3B%0Aif(swstate%20%3D%3D%3D%201)%7B%0A%24('%23toggle').bootstrapToggle('on')%3B%7D%0Aelse%7B%0A%24('%23toggle').bootstrapToggle('off')%3B%7D%0A%0A%7D)%3B%0A%2F%2F%20New%20socket%20connected%2C%20display%20new%20count%20on%20page%0Asocket.on('users%20connected'%2C%20function(data)%7B%0A%24('%23usersConnected').html('%3Cstrong%3EUsers%20Connected%3A%3C%2Fstrong%3E%20'%20%2B%20data)%0A%7D)%0A%0Asocket.on('led'%2C%20function%20(data)%20%7B%0Adocument.getElementById(%22inputSlider%22).value%20%3D%20data.value%3B%0Adocument.getElementById(%22outputText%22).innerHTML%20%3D%20data.value%3B%0A%7D)%3B%0A%0Afunction%20showValue(newValue)%0A%7B%0Adocument.getElementById(%22outputText%22).innerHTML%3DnewValue%3B%0Asocket.emit('led'%2C%20%7B%20value%3A%20newValue%20%7D)%3B%0A%7D%0Afunction%20swValue(swstate)%0A%7B%0Adocument.getElementById(%22swval%22).innerHTML%3Dswstate%3B%0A%7D%0A%2F%2F%7D)%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
</body>
</html>

Below is the Code for the ESP8266 NodeMCU programmed on the Arduino IDE (v1.8.5) environment:

#include <ESP8266WiFi.h>          //ESP8266 Core WiFi Library (you most likely already have this in your sketch)

//#include <DNSServer.h> //Local DNS Server used for redirecting all requests to the configuration portal
#include <ESP8266WebServer.h> //Local WebServer used to serve the configuration portal
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
#include <ESP8266mDNS.h>
#include <SocketIoClient.h>
#include <ArduinoJson.h>

#define LED 13
// Pin D7 on NodeMCU
#define LEDPWM 12
// Pin D6 on NodeMCU

const char* mdnsName = "esp8266";

// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;

ESP8266WebServer webServer(80);

SocketIoClient socket;

void light(const char * payload, size_t length) {
String msg;
//String text = String((char *) &payload[0]);
msg = String((char*)payload);
if(msg=="1"){ digitalWrite(LED,HIGH); Serial.println("LED ON");}else{digitalWrite(LED,LOW); Serial.println("LED OFF");}
Serial.println(msg);
}

void lightpwm(const char * payload, size_t length) {

DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(4) + 100);

//String text = String((char *) &payload[0]);
//String msg2 = String((char*)payload);

JsonObject& root = jsonBuffer.parseObject(String((char*)payload));
int pwmval = root["value"];
//Serial.println("PWM LED");
analogWrite(LEDPWM, pwmval);
}

String responseHTML = ""
"<!DOCTYPE html><html><head><title>Hello World!</title></head><body>"
"

<h1>Hello World!</h1>


All requests are redirected here.

</body></html>";

//flag for saving data
bool shouldSaveConfig = false;

void configModeCallback (WiFiManager *myWiFiManager) {
Serial.println("Entered config mode");
Serial.println(WiFi.softAPIP());
Serial.println(myWiFiManager->getConfigPortalSSID());
}

//callback notifying us of the need to save config
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}

void setup() {
// put your setup code here, to run once:
pinMode(LED, OUTPUT);
pinMode(LEDPWM, OUTPUT);
digitalWrite(LED, LOW);
Serial.begin(115200);
WiFiManager wifiManager;
//reset settings - for testing
//wifiManager.resetSettings();
wifiManager.setConfigPortalTimeout(360);
wifiManager.setDebugOutput(false);

wifiManager.setAPCallback(configModeCallback);
//WiFiManager.setHostname(mdnsName);
//fetches ssid and pass and tries to connect
//if it does not connect it starts an access point with the specified name
//here "AutoConnectAP"
//and goes into a blocking loop awaiting configuration
if(!wifiManager.autoConnect("Allen_LED", "1234pass")) {
Serial.println("failed to connect and hit timeout");
//reset and try again, or maybe put it to deep sleep
ESP.reset();
delay(1000);
}
wifiManager.setSaveConfigCallback(saveConfigCallback);

//if you get here you have connected to the WiFi
Serial.println("Connected!... YES!");
Serial.println("Local IP is: ");
Serial.println(WiFi.localIP());
WiFi.hostname(mdnsName);
if (!MDNS.begin(mdnsName, WiFi.localIP())) {
// Start the mDNS responder for esp8266.local
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
// Add service to MDNS-SD
MDNS.addService("http", "tcp", 80);
MDNS.addServiceTxt("http", "tcp", "url", "http://esp8266.local");

socket.begin("www.domain.com", 3000, "/socket.io/?transport=websocket");
//socket.on("event", event);
socket.on("swstate", light);
socket.on("led", lightpwm);

// HTTP Basic Authorization (optional)
//webSocket.setAuthorization("username", "password");

//socket.emit("plainString", "\"this is a plain string\"");
//socket.emit("swstate", "1");

// replay to all requests with same HTML
webServer.onNotFound([]() {
webServer.send(200, "text/html", responseHTML);
});
webServer.begin();
}

void loop() {
// put your main code here, to run repeatedly:
dnsServer.processNextRequest();
webServer.handleClient();
socket.loop();
}

The end result will look like the screenshot below where the Button Switch controls Pin D7 on the NodeMCU, and the Slider will create a PWM Dimmer on Pin D6, which you can connect an LED to see the brightness level correspond to the slider in just about real-time. One side note: Make sure you replace domain.com in all the code above to reflect where your node.js server is running, localhost, ip, domain, hostname etc.