How to machine an aluminum housing on the Tormach 770!
Thermal printer + Aluminum Housing + Arduino Code = TASKMASTER!
Digital reminders are easy to generate AND easy to ignore so, we’re bringing them back into the physical world. We didn’t want to use solid billet so we glued 5 aluminum chunks together using Locktite H800 and machined it into one! This project is a great example of using additive and subtractive machining to get the part you want!
Instructional Video:
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | // ********************************************************* // **** Include headers for all required libraries here **** // ********************************************************* #include <Adafruit_Thermal.h> #include <ESP8266WiFi.h> #include <ArduinoJson.h> #include <SoftwareSerial.h> // ********************************************************* // **** Define data classes here **** // ********************************************************* /* Used to hold the couple bits of HTTP response header info we want and the body of the response */ class HTTP_Response { public: String hdr_status = ""; bool is_chunked = true; long body_len = 0; String body = ""; }; // ************************************************************* // **** Put #defines, constants, static strings, etc here **** // ************************************************************* /* A couple macros to make adding multilevel debug statements easier */ #define DEBUG_LEVEL_1(x) {if (DEBUG_LEVEL > 0) {Serial.print(x);}} #define DEBUG_LEVEL_2(x) {if (DEBUG_LEVEL > 1) {Serial.print(x);}} /* Set this to 0 for no debug messages, 1 for basic debug messages, and 2 for all debug messages */ const int DEBUG_LEVEL = 2; /* Your wifi SSID and password */ const char* WIFI_SSID = "replacewithyourssid"; const char* WIFI_PASS = "replacewithyourpassword"; /* Your Adafruit IO username, AIO key, and feed name */ const char* AIO_USERNAME = "replacewithusername"; const char* AIO_KEY = "replacewithyourAIOkey"; const char* AIO_FEED1 = "smwthermal"; /* you can add more feeds here and pass them into the data functions to manage multiple feeds */ /* Adafruit IO host and SSL port. Should not have to change these */ const char* AIO_HOST = "io.adafruit.com"; const int AIO_SSL_PORT = 443; /* Adafruit IO SSL Certificate fingerprint, taken from Adafruit IO library. Used to make sure the TLS/SSL connection is using the proper Adafruit certificate (a security thing)*/ const char* AIO_SSL_FINGERPRINT = "AD 4B 64 B3 67 40 B5 FC 0E 51 9B BD 25 E9 7F 88 B6 2A A3 5B"; /* Adafruit IO API root path */ const char* AIO_API_ROOT_PATH = "/api/v2/"; /* Pin definitions for software serial port that drives the thermal printer */ #define RX_PIN 4 #define TX_PIN 5 // ********************************************************* // **** Declare global variables here **** // ********************************************************* SoftwareSerial printer_port(RX_PIN, TX_PIN); Adafruit_Thermal printer(&printer_port); WiFiClientSecure aio_client; // ************************************************************************************** // **** Arduino setup() function - put one time initialization and setup code here **** // ************************************************************************************** void setup() { // Huzzah 8266 board has a general purpose red LED attached to Digital pin 0 so set that pin as an output pinMode(0,OUTPUT); // Open the USB serial port which will be used to print debug and status messages to the Arduino IDE serial monitor Serial.begin(115200); // Wait for the serial port initialization to complete while(!Serial); // Open the serial port that talks to the thermal printer printer_port.begin(19200); //JWS: this was 19200 // Wait for the serial port initialization to complete while(!printer_port); // Initialize the printer printer.begin(); // Connect to WiFi DEBUG_LEVEL_1("Connecting to WiFi "); WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() != WL_CONNECTED) { delay(250); DEBUG_LEVEL_1("."); } DEBUG_LEVEL_1("\nWiFi connected\nIP address: " + WiFi.localIP().toString() + "\n"); } // ************************************************************* // **** Arduino loop() function - put main loop code here **** // ************************************************************* void loop() { // Query the Adafruit IO feed defined at the top of the file, restricting the response to just the id and value fields HTTP_Response resp; digitalWrite(0,0); GetLastData(AIO_FEED1, &resp); digitalWrite(0,1); // Create the ArduinoJson objects needed to parse the response DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.parseObject(resp.body); // If the parsing did not succeed, print out an error message to the serial port // If it did succeed, process the results if (root.success() == false) { DEBUG_LEVEL_1("Error parsing response body as JSON\n"); DEBUG_LEVEL_2("Body:\n" + resp.body + "\n"); } else { // Extract the id and value fields into separate variables String id = root["id"]; String value = root["value"]; DEBUG_LEVEL_1(String("id:") + id + "\n"); DEBUG_LEVEL_1(String("value:\n") + value + "\n"); // If we actually got a message, do something with it if (id.length() > 0 && value.length() > 0) { // Print the body of the response to the thermal printer PrintToThermalPrinter( value, 'S', 'L', false, false ); printer.feed(); printer.feed(); // Delete the message from Adafruit IO DeleteData( AIO_FEED1, id, &resp); } } // Check for messages every 5 seconds delay(5000); /* other main loop code here .... */ } // ************************************************************* // **** Gets the first message in a given feed **** // ************************************************************* void GetFirstData( String aio_feed, HTTP_Response* resp ) { DEBUG_LEVEL_1("Getting first message in feed " + aio_feed + "\n"); SendQueryAndGetResponse("GET", aio_feed, "first", "id,value", resp); } // ************************************************************* // **** Gets the last message in a given feed **** // ************************************************************* void GetLastData( String aio_feed, HTTP_Response* resp ) { DEBUG_LEVEL_1("Getting last message in feed " + aio_feed + "\n"); SendQueryAndGetResponse("GET", aio_feed, "last", "id,value", resp); } // ************************************************************* // **** Gets all messages in a given feed **** // ************************************************************* void GetAllData( String aio_feed, HTTP_Response* resp ) { DEBUG_LEVEL_1("Getting all messages in feed " + aio_feed + "\n"); SendQueryAndGetResponse("GET", aio_feed, "", "id,value", resp); } // ************************************************************* // **** Deletes a message with a given ID in a given feed **** // ************************************************************* void DeleteData( String aio_feed, String message_id, HTTP_Response* resp ) { DEBUG_LEVEL_1("Deleting message in feed " + aio_feed + " with id=" + message_id + "\n"); SendQueryAndGetResponse("DELETE", aio_feed, message_id, "", resp); // Check the response and if there was an error deleting the message, print an error to the serial port (dependent on DEBUG_LEVEL) if (resp->hdr_status.equalsIgnoreCase("200 OK") == false) { DEBUG_LEVEL_1("Error deleting message with id=" + message_id + "\nStatus=" + resp->hdr_status + "\n"); } } // ************************************************************************************** // **** Makes a connection to the Adafruit IO web service, transmits the given **** // **** request, receives the response, and parses out the header info we want **** // **** and response body. Debug/logging messages are dumped out the serial port **** // **** as the function works through all the steps. **** // ************************************************************************************** bool SendQueryAndGetResponse(String query_type, String aio_feed, String message_id, String include_fields, HTTP_Response* resp) { // Create a secure connection to the Adafruit IO site DEBUG_LEVEL_1(String("Connecting to https://") + AIO_HOST + "\n"); if (aio_client.connect(AIO_HOST, AIO_SSL_PORT) == true) { DEBUG_LEVEL_1("Connected\n"); } else { DEBUG_LEVEL_1("Connection failed\n"); return false; } // Verify the fingerprint of the SSL certificate being used by the Adafruit site as a bit of a // security measure to make sure we weren't somehow redirected elsewhere /* JWS commenting this out: if (aio_client.verify(AIO_SSL_FINGERPRINT, AIO_HOST) == false) { DEBUG_LEVEL_1("SSL certificate doesn't match expected certificate\n"); return false; } */ // Build up the URL to query the proper Adafruit IO feed for the proper user and using the given data retrieval function (ie. first, next, previous, last) String url = String(AIO_API_ROOT_PATH) + AIO_USERNAME + "/feeds/" + aio_feed + "/data"; // If we were given a message id, tack it on if (message_id.length() > 0) { url += "/" + message_id; } // If we were given a list of specific fields to limit the response to, tack those onto the URL if (include_fields.length() > 0) { url += "?include=" + include_fields; } // Build up the full HTTP GET request with the above URL and the required headers String get_req = query_type + " " + url + " HTTP/1.1\r\n" + "Host: " + AIO_HOST + "\r\n" + "User-Agent: ESP8266\r\n" + "X-AIO-KEY: " + AIO_KEY + "\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "\r\n"; // Print the GET request to the serial monitor if needed and transmit it to Adafruit IO // Putting the debug print first is by design because printing to the serial port takes some // finite amount of time and while it might not matter, it's probably best to drop into the // response parsing code immediately after sending the request out and not risk missing incoming // characters due to being tied up printing to the serial port. DEBUG_LEVEL_1("Sending request\n"); DEBUG_LEVEL_2(get_req); aio_client.print(get_req); DEBUG_LEVEL_1("Waiting for response\n"); // The "Connection: close" header in the GET request tells the Adafruit IO server to close it's connection with us // when it's finished returning the response. That lets us receive data in a loop until the connection is closed. // First response data we encounter is the response header which is processed differently than the body bool getting_headers = true; while (aio_client.connected()) { // If there is some data available, process it while (aio_client.available()) { String line; char c; if (getting_headers == true) { // Headers are divided into CRLF delimited lines. Read data until a newline character is encountered then process the line // We pull data from the aio_client stream one character at a time instead of using readStringUntil('\n') because that function // doesn't put the terminator character in the string and also this way we can dump *exactly* what comes in to the serial port // for debugging purposes. line = ""; c = '\0'; while (c != '\n') { c = aio_client.read(); DEBUG_LEVEL_2(c); line += c; } // If we run into an empty line, we've reached the end of the header section of the response if (line == "\r\n") { getting_headers = false; DEBUG_LEVEL_2("Done Headers\n"); DEBUG_LEVEL_2("Raw Response:\n"); } else { // Parse the current header line. If it's interesting to us, the relevant HTTP_Response structure field will be set if (ParseHeaderLine(line, resp) == false) { return false; } } } else // if not doing the header, process the response body { // The body of the response may be a simple series of characters with no special processing required or it may be // "chunked" into distinct segments, each of which has a leading indicator of the chunk size (in hex) if (resp->is_chunked == true) { // First time through here we will have just finished the header section and be sitting at the first chunk size. // We don't actually care much about the chunk size though. We'll just skip the chunk size rows and concatenate the // body content rows until there's nothing left to process. Since the rows come in size/content pairs, if we // pull two rows from the stream here, every time we get back here we should be properly aligned to pull the next two. // Skip chunk size line c = '\0'; while (c != '\n') { c = aio_client.read(); DEBUG_LEVEL_2(c); } // Read body content line and append to response body line = ""; c = '\0'; while (c != '\n') { c = aio_client.read(); DEBUG_LEVEL_2(c); line += c; } // When we get a chunked body, the above will leave the CRLF characters tacked onto the end of 'line' which we don't want // so strip them off before appending 'line' to the response body line.remove(line.length() - 2); resp->body += line; } else { // For non-chunked data, just append all available characters to the response body for (int i = 0; i < aio_client.available(); i++) { resp->body += aio_client.read(); } } } // process response body } // while (aio_client.available()) } // while (aio_client.connected()) // We should have the entire response body now so set the body_len field in the HTTP_Response structure resp->body_len = resp->body.length(); DEBUG_LEVEL_2("Processed response:\n"); DEBUG_LEVEL_2(resp->body + "\n"); DEBUG_LEVEL_1("Response received\n"); } // ************************************************************* // **** Parses one line of the header section of an HTTP **** // **** response, extracting a couple bits of information **** // **** and populating the relevant fields of resp **** // ************************************************************* bool ParseHeaderLine(String line, HTTP_Response* resp) { // Headers could be upper, lower, or mixed case, so convert to all upper case for string compare operations line.toUpperCase(); // Check for a Transfer-Encoding header. We need this mostly to determine if the body is split into chunks or not. if (line.indexOf("TRANSFER-ENCODING") > -1) { if (line.indexOf("CHUNKED") > -1) { resp->is_chunked = true; } else if (line.indexOf("IDENTITY") > -1) { resp->is_chunked = false; } else { // We don't support any other encodings (other would generally be some sort of compression) return false; } } // Check for a Content-Length header. If present, it specifies the length of the body but also implies no chunking if (line.indexOf("CONTENT_LENGTH") > -1) { resp->is_chunked = false; resp->body_len = line.substring(line.indexOf(":") + 1).toInt(); } // Check for a Status header if (line.indexOf("STATUS") > -1) { resp->hdr_status = line.substring(line.indexOf(":") + 1); resp->hdr_status.trim(); } return true; } // ************************************************************* // **** Prints the given string to the thermal printer **** // ************************************************************* void PrintToThermalPrinter( String msg, char text_size, char justification, bool bold, bool underline ) { DEBUG_LEVEL_1("Printing message to thermal printer\n"); // Check paper status before trying to print if (printer.hasPaper() == false) { DEBUG_LEVEL_1("Printer has no paper!\n"); return; } printer.justify(justification); printer.setSize(text_size); if (bold == true) { printer.boldOn(); } else { printer.boldOff(); } if (underline == true) { printer.underlineOn(); } else { printer.underlineOff(); } printer.println("TASK/ASIGNEE/DUE DATE"); printer.print(msg); printer.println(); printer.println(); printer.println("_____________________"); } |
Related Videos & Resources:
Arduino Automation! Stepper-Driven Automatic Cutting Saw
Arduino Stepper iPad Height Machine!
Arduino Vibe Bowl Screw Feeder