A web server for monitoring and controlling a MakerBot Replicator over the USB host and ethernet.

Dependencies:   IAP NTPClient RTC mbed-rtos mbed Socket lwip-sys lwip BurstSPI

Fork of LPC1768_Mini-DK by Frank Vannieuwkerke

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers makerbot.cpp Source File

makerbot.cpp

00001 // Copyright (c) 2013, jake (at) allaboutjake (dot) com
00002 // All rights reserved.
00003 // 
00004 // Redistribution and use in source and binary forms, with or without
00005 // modification, are permitted provided that the following conditions are met:
00006 //     * Redistributions of source code must retain the above copyright
00007 //       notice, this list of conditions and the following disclaimer.
00008 //     * Redistributions in binary form must reproduce the above copyright
00009 //       notice, this list of conditions and the following disclaimer in the
00010 //       documentation and/or other materials provided with the distribution.
00011 //     * The name of the author and/or copyright holder nor the
00012 //       names of its contributors may be used to endorse or promote products
00013 //       derived from this software without specific prior written permission.
00014 // 
00015 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00016 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00017 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00018 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, AUTHOR, OR ANY CONTRIBUTORS
00019 // BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
00020 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
00021 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
00022 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00023 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
00024 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00025 
00026 // DESCRIPTION OF FILE:
00027 //
00028 // This file contains a C++ object for communicating with a Makerbot over a USB
00029 // host serial.
00030 //
00031 
00032 #include "makerbot.h"
00033 #include "mbed.h"
00034 
00035 // Constructor
00036 Makerbot::Makerbot(USBHostSerial* hostSerial) {
00037     // Save the serial object
00038     serial = hostSerial;
00039     
00040     // Initialize caced values to NULL/zero, etc.
00041     machineName=NULL;
00042     buildName=NULL;
00043     machineVersion=0.0;
00044 }
00045 
00046 // Helper function to calculate the iButton CRC of a buffer
00047 uint8_t Makerbot::CalculateCRC(uint8_t* data, int length) {
00048     uint8_t val = 0;
00049     for (int x=0; x<length; x++) { 
00050         val = crctab[val ^ data[x]];
00051     } 
00052     return val;
00053 }
00054 
00055 // Send a packet with the given payload.
00056 int Makerbot::send_packet(uint8_t* payload, int payload_length) {
00057     // Verify payload length within bounds
00058     if (payload_length > MAXIMUM_PAYLOAD_LENGTH)
00059         return PACKET_TOO_BIG;
00060     
00061     // Calculate teh checksum    
00062     uint8_t checksum = CalculateCRC(payload, payload_length);
00063     
00064     // Send the data
00065     serial->putch(HEADER);
00066     serial->putch(payload_length);
00067     serial->writeBuffer(payload, payload_length);
00068     serial->putch(checksum);   
00069     
00070     // Return 0 for success;
00071     return 0;
00072 }
00073 
00074 
00075 // Receive a packet and fill the payload buffer with the packet's payload.
00076 int Makerbot::recv_packet(uint8_t* payload, int buffer_length) {
00077     // Wait for a packet
00078     while (!serial->available()) { Thread::wait(100); }
00079     
00080     // Check for the header
00081     if (serial->getch() != HEADER) {
00082         DBG_INFO("recv_packet error: Header not found");
00083         return -1;
00084     }
00085 
00086     // Get the payload length
00087     int length = serial->getch();
00088     if (length > buffer_length) {
00089         DBG_INFO("recv_packet: Payload is larger then available buffer");
00090         return -1;
00091     }
00092 
00093     // Get the data with the given length
00094     for (int x=0; x<length; x++) {
00095         payload[x] = serial->getch();
00096     }
00097     
00098     // Get the checksum and verify the data we got matches the checksum
00099     int checksum = serial->getch();
00100     if (checksum < 0 || checksum != CalculateCRC(payload, length)) {
00101         DBG_INFO("recv_packet: Checksum error");
00102         return -1;
00103     } 
00104    
00105     // Return the length of the packet we received.
00106     return length;
00107 }
00108 
00109 // Display a message to to the Makerbot's LCD
00110 int Makerbot::displayMessageToLCD(uint8_t options, uint8_t xPos, uint8_t yPos, uint8_t timeout, char*  message) {
00111     // Get the length of the message
00112     int msgLen = strlen(message);
00113     
00114     // The packet uses 5 bytes for the command and the other parameters
00115     // Make sure there is enough remaining space for the given message.
00116     if (msgLen > MAXIMUM_PAYLOAD_LENGTH - 5) {
00117         DBG_INFO("displayMessageToLCD: error: message too long.");
00118         return -1;
00119     }
00120     
00121     // Build the packet
00122     uint8_t packet[MAXIMUM_PAYLOAD_LENGTH+3];
00123     packet[0] = 149; // LCD
00124     packet[1] = options;
00125     packet[2] = xPos;
00126     packet[3] = yPos;
00127     packet[4] = timeout;
00128     for (int x=0; x<msgLen; x++) {
00129         packet[5+x] = message[x];
00130     }
00131     packet[5+msgLen] = 0x00; //terminate
00132     
00133     // Send the packet and receive the response
00134     mutex.lock();
00135     send_packet(packet, msgLen+6);    
00136     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];
00137     recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00138     mutex.unlock();
00139     
00140     // Return 0 if success (easier for error handling since SUCCESS is not zero)    
00141     if (response[0]==SUCCESS) return 0;
00142     
00143     // Otherwise return the actual response code.
00144     return response[0];
00145 }
00146 
00147 // Get the version of the makerbot
00148 float Makerbot::getMakerbotVersion() {
00149     //If we have a cache of the version, just return it
00150     if (machineVersion) return machineVersion;
00151     
00152     //Build the packet
00153     uint8_t packet[3];
00154     packet[0]=0x00;
00155     packet[1]=0x00;
00156     packet[2]=0x64;
00157     
00158     //Send the packet and get the response packet
00159     mutex.lock();
00160     send_packet(packet, 3);       
00161     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];   
00162     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00163     mutex.unlock();
00164     
00165     //Make sure we got back the right payload length
00166     if (length != 3) {
00167         DBG_INFO("getMakerbotVersion: Expected 2 byte response. got %d", length);
00168         return 0; //Version=0 is an error in this case
00169     }
00170     
00171     //Convert the version to a float and return
00172     uint16_t versionData = *((uint16_t*)&response[1]);
00173     machineVersion = ((float)(versionData / 100)) + (((float)(versionData % 100)) / 10);
00174     return machineVersion;
00175 }
00176 
00177 char* Makerbot::getBuildName() {
00178     // One byte commend
00179     uint8_t cmd = 0x14; // 20 = Pause/Resume command
00180 
00181     // Send the Packet, get the response
00182     mutex.lock();    
00183     send_packet(&cmd, 1);
00184     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00185     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00186     mutex.unlock();
00187 
00188     if (length > 1 && response[0]==SUCCESS) {
00189         //if we have an old build name, trash it
00190         if (buildName) {
00191             free(buildName);
00192             buildName = NULL;
00193         }
00194                 
00195         buildName = (char*)malloc(length-1);
00196         strncpy(buildName, (char*)response+1,length-1);
00197         return buildName;
00198     }
00199     
00200     return NULL;
00201 }
00202 
00203 // Start a print job from the SD card with the given filename
00204 int Makerbot::playbackCapture(char* filename) {   
00205     // Build the packet
00206     uint8_t packet[14];
00207     packet[0]=0x10; //Playback = 16
00208     
00209     // Copy over the filename into the packet one byte at a time
00210     // TODO: replace with memcpy?
00211     int length = 1;
00212     for (int x=0; x<12; x++) {
00213         packet[x+1] = filename[x];
00214         length++;
00215         if (filename[x]==0) break;
00216     }
00217     
00218     // Send the packet, get the response
00219     mutex.lock();
00220     send_packet(packet, length);           
00221     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];
00222     int recvLength = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00223     mutex.unlock();
00224     
00225     // Check for response success
00226     if (recvLength > 0 && response[0] == SUCCESS) {
00227             return 0;
00228     }
00229     
00230     //If we got here, there was a problem somewhere
00231     return -1;
00232 }
00233 
00234 // Get machine name from bot's EEPROM
00235 char* Makerbot::getMachineName() {
00236     // If we have a cached name, return it
00237     if (machineName) return machineName;
00238     
00239     // Build the request packet
00240     uint8_t payload[4];
00241     payload[0]= 0xC; //read eprom = 12
00242     *((uint16_t*)&payload[1])= MACHINE_NAME_OFFSET;    
00243     payload[3]= 16; //length of machine name;
00244    
00245     // Send the packet, wait for response.
00246     mutex.lock();
00247     send_packet(payload, 4);    
00248     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00249     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00250     mutex.unlock();
00251     
00252     // Return
00253     if (length > 0 && response[0] == SUCCESS) {        
00254         //create a buffer to hold the machine name;
00255         machineName = (char*)malloc(length+1);
00256         
00257         memcpy(machineName, response+1, length-1);
00258         machineName[length-2] = '\0'; //terminate
00259         
00260         return machineName;
00261     }
00262 
00263     // Get machine name failed for some reason
00264     DBG_INFO("getMachineName: unsuccessful");
00265     return NULL;
00266 }   
00267 
00268 int Makerbot::getEEPROMByte(uint16_t offset) {     
00269     // Build the request packet
00270     uint8_t payload[4];
00271     payload[0]= 0xC; //read eprom = 12
00272     *((uint16_t*)&payload[1])= offset;    
00273     payload[3]= 1; //length of tool count (byte)
00274    
00275     // Send the packet, wait for response.
00276     mutex.lock();
00277     send_packet(payload, 4);    
00278     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00279     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00280     mutex.unlock();
00281     
00282     // Return
00283     if (length == 2 && response[0] == SUCCESS) {               
00284         return response[1];
00285     }
00286 
00287     // Get machine name failed for some reason
00288     DBG_INFO("getEEPROMByte: unsuccessful");
00289     return 0;
00290 }
00291 
00292 // Get tool count from bot's EEPROM
00293 int Makerbot::getToolCount() { 
00294     return getEEPROMByte(TOOL_COUNT_OFFSET);
00295 }
00296 
00297 int Makerbot::hasPlatform() {
00298     return getEEPROMByte(HBP_PRESENT_OFFSET);
00299 }
00300 
00301 // Flush the buffer.  Used to synchronize the stream
00302 void Makerbot::flushInputChannel() {
00303     mutex.lock();
00304     while (1) {
00305         if (serial->available()) 
00306             serial->getch();
00307         else
00308             break;
00309     }
00310     mutex.unlock();
00311 }
00312 
00313 // Send pause/resume command
00314 int Makerbot::pauseResume() {
00315     // One byte commend
00316     uint8_t cmd = 0x08; // 8 = Pause/Resume command
00317 
00318     // Send the Packet, get the response
00319     mutex.lock();    
00320     send_packet(&cmd, 1);
00321     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00322     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00323     mutex.unlock();
00324     
00325     // Return success status
00326     if (length == 1 && response[1] == SUCCESS) return 0;
00327     return -1;
00328 }
00329 
00330 // Send command to cancel build command
00331 int Makerbot::abortImmediately() {
00332     // One byte commend
00333     uint8_t cmd = 0x07; // 7 = Abort command
00334 
00335     // Send the Packet, get the response
00336     mutex.lock();    
00337     send_packet(&cmd, 1);
00338     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00339     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00340     mutex.unlock();
00341     
00342     // Return success status
00343     if (length == 1 && response[1] == SUCCESS) return 0;
00344     return -1;
00345 }
00346 
00347 // Retrieve the current temperature for the given tool
00348 int Makerbot::getToolTemperature(uint8_t toolIndex) {
00349     // One byte commend
00350     uint8_t payload[3];
00351     payload[0] = 0x0A; // 7 = Abort command
00352     payload[1] = toolIndex;
00353     payload[2] = 0x2; //get toolhead temperature
00354     
00355     // Send the Packet, get the response
00356     mutex.lock();    
00357     send_packet(payload, 3);
00358     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00359     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00360     mutex.unlock();
00361     
00362    
00363     // Return success status
00364     if (length == 3 && response[0] == SUCCESS) {
00365         return *((uint16_t*)&response[1]);
00366     }
00367     return -1;
00368 }
00369 
00370 // Return the setpoint for the given tool
00371 int Makerbot::getToolSetPoint(uint8_t toolIndex) {
00372     // One byte commend
00373     uint8_t payload[3];
00374     payload[0] = 0x0A; // 7 = Abort command
00375     payload[1] = toolIndex;
00376     payload[2] = 0x20; //get toolhead set point
00377     
00378     // Send the Packet, get the response
00379     mutex.lock();    
00380     send_packet(payload, 3);
00381     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00382     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00383     mutex.unlock();
00384     
00385    
00386     // Return success status
00387     if (length == 3 && response[0] == SUCCESS) {
00388         return *((uint16_t*)&response[1]);
00389     }
00390     return -1;
00391 }
00392 
00393 
00394 // Retrieve the Platform temperature for the given tool
00395 int Makerbot::getPlatformTemperature(uint8_t toolIndex) {
00396     // One byte commend
00397     uint8_t payload[3];
00398     payload[0] = 0x0A; // 7 = Abort command
00399     payload[1] = toolIndex;
00400     payload[2] = 0x1E; //get toolhead temperature
00401     
00402     // Send the Packet, get the response
00403     mutex.lock();    
00404     send_packet(payload, 3);
00405     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00406     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00407     mutex.unlock();
00408     
00409    
00410     // Return success status
00411     if (length == 3 && response[0] == SUCCESS) {
00412         return *((uint16_t*)&response[1]);
00413     }
00414     return -1;
00415 }
00416 
00417 // Return the platform setpoint for the given tool
00418 int Makerbot::getPlatformSetPoint(uint8_t toolIndex) {
00419     // One byte commend
00420     uint8_t payload[3];
00421     payload[0] = 0x0A; // 7 = Abort command
00422     payload[1] = toolIndex;
00423     payload[2] = 0x21; //get toolhead set point
00424     
00425     // Send the Packet, get the response
00426     mutex.lock();    
00427     send_packet(payload, 3);
00428     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];    
00429     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00430     mutex.unlock();
00431     
00432    
00433     // Return success status
00434     if (length == 3 && response[0] == SUCCESS) {
00435         return *((uint16_t*)&response[1]);
00436     }
00437     return -1;
00438 }
00439 
00440 // Get the build state.  Note that we added a "BUILD_STATE_ERROR" value to detect errors in the return value.
00441 Makerbot::MakerbotBuildState Makerbot::getBuildState() {
00442     MakerbotBuildState state;
00443     
00444     // One byte command
00445     uint8_t cmd = 0x18; //24=get build statistics    
00446     
00447     // Send the packet, get the response
00448     mutex.lock();    
00449     send_packet(&cmd, 1);
00450     uint8_t response[MAXIMUM_PAYLOAD_LENGTH];
00451     int length = recv_packet(response, MAXIMUM_PAYLOAD_LENGTH);
00452     mutex.unlock();
00453     
00454     
00455     // Check for success
00456     if (response[0] == SUCCESS) {
00457         // Copy the data to the return struct & return
00458         memcpy(&state, response+1, sizeof(MakerbotBuildState));
00459         return state;
00460     }
00461     
00462     // Otherwise return error state
00463     state.build_state = BUILD_STATE_ERROR; //error
00464     return state; 
00465 }
00466 
00467 char* Makerbot::humanReadableBuildState(Makerbot::MakerbotBuildState state) {   
00468     switch (state.build_state) {                        
00469             case Makerbot::NO_BUILD:
00470                 return "No Build";
00471 
00472             case Makerbot::BUILD_RUNNING:
00473                 return "Build Running";                
00474             
00475             case Makerbot::BUILD_FINISHED_NORMALLY:
00476                 return "Build Finished";                
00477             
00478             case Makerbot::BUILD_PAUSED:
00479                 return "Build Paused";                
00480             
00481             case Makerbot::BUILD_CANCELLED:
00482                 return "Build Cancelled";                
00483             
00484             case Makerbot::BUILD_SLEEPING:
00485                 return "Build Sleeping";                
00486                        
00487             case Makerbot::BUILD_STATE_ERROR:
00488             default:
00489                 return "Unknown build status or status error";                
00490     }
00491 }
00492 
00493 // Clean up any objects created, just for good measure.
00494 Makerbot::~Makerbot() {
00495     if (machineName) {
00496         free(machineName);
00497     }
00498     if (buildName) {
00499         free(buildName);
00500     }
00501 }