Reading serial input

Hello,
It is posible to read a serial device, like cod bar scanner or a usb to serial interface with particle photon?
Thanks.

Yes. I have a project on my desk where I connect a serial barcode scanner (RS232 I believe) via a serial DB9 connector adapter (which I believe has a Serial-to-TTL chip on it). That adapter is then connected to TX/RX on my photon. I am able to read a barcode scan and send it to my SQL database (via custom http APi) and cloud publish. The breadboard setup is very simple and functions well.

Thanks for your answer, i am going to search info about Serial to TTL chip. Do you read character by character in a loop, the information from your serial barcode scanner in your firmware?

FYI, if you’re going to use the UART (a.k.a. serial port) to read a sensor, I strongly recommend the lib developed by @rickkas7: Particle Serial Interrupt. It will save you many headaches.

1 Like

I also use rickkas7’s SerialBufferRK library. It works well. Here’s the code I use to read the barcode and then send to various enpoints including a custom API written for .Net core and a Particle.publish():

// This #include statement was automatically added by the Particle IDE.
#include <PublishQueueAsyncRK.h>

// This #include statement was automatically added by the Particle IDE.
#include <HttpClient.h>

#include "Particle.h"

#include "SerialBufferRK.h"

//**************************************************
//System modes and threading setup.
//**************************************************
SYSTEM_THREAD(ENABLED);

//**************************************************
//Publish Queue/Buffer
//**************************************************
retained uint8_t publishQueueRetainedBuffer[2048];
PublishQueueAsync publishQueue(publishQueueRetainedBuffer, sizeof(publishQueueRetainedBuffer));


//**************************************************
//http client variables
//**************************************************
HttpClient http;

// Headers currently need to be set at init, useful for API keys etc.
http_header_t headers[] = {
    { "Content-Type", "application/json" },
    //  { "Accept" , "application/json" },
    { "Accept" , "*/*"},
    { NULL, NULL } // NOTE: Always terminate headers will NULL
};

http_request_t request;
http_response_t response;

//**************************************************
//Other objects and forward declarations
//**************************************************
SerialLogHandler logHandler;            //Log output handler
SerialBuffer<4096> serBuf(Serial1);     //Serial buffer object

void resetSequence();   //Forward Declaration

//**************************************************
//primary datatype variables (int, bool, etc)
//**************************************************
unsigned long lastRead = 0;     //Last time a char was read.
int readTimeout = 500;          //How long to wait for a full barcode in ms.
bool terminateNow = false;      //Set high if a terminating char is encountered in a barcode read (i.e. CR, LF, Tab).

uint8_t charsRead = 0;          //Number of chars read in this barcode sequence.
uint32_t totalChars = 0;        //Total chars read since boot.
uint32_t totalBarcodes = 0;     //Total barcodes read since boot.

uint8_t lastCharIndex = 0;      //Holds the index of the last char read. Used to determine if read timeout should be set.

char pubString[255];            //String to use for publishing to endpoint.
char barCode[255];              //String to hold current barcode.

int LED = D7;                   //Use the built-in LED as a read timeout indicator.


void setup() {
	Serial.begin();             //Start USB serial for monitoring.
	Serial1.begin(9600);        //Start UART serial for reading barcode data from RS232-capable barcode scanner.
	
	Serial.println("Waiting for Serial Monitor...");
	delay(5000);    //Delay to allow time to connect serial monitor.
	Serial.println("Resetting Serial1 buffer...");

	resetSequence();    //Purge serial buffer.
	
	pinMode(LED, OUTPUT);   //Set built-in D7 LED to use as an output.
	
	Serial.println("Waiting for next barcode scan...");
}

void loop() {
	//Read serial buffer until empty.
	while(true) {
		int c = serBuf.read();  //Read one char at a time.
		if (c < 0) {            //If serial buffer returns a negative value, the buffer is empty.
			break;
		}
		
		//If there is a char to read, and this is the first char, then output the diagnostic message prefix.
		if (charsRead == 0) {
            Serial.print("Chars Received: ");
        }
		
		//Look for special characters that indicate the end of a barcode sequence.
		if ((char)c == '\r') {  //Detect carriage returns as the end of a barcode sequence.
		    Serial.println();
		    Serial.println("Carriage return detected.");
		    terminateNow = true;
		}
		else if ((char)c == '\n') {  //Detect line feeds as the end of a barcode sequence.
		    Serial.println();
		    Serial.println("New line detected.");
		    terminateNow = true;
		}
		else if ((char)c == '\t') {  //Detect horizontal tabs as the end of a barcode sequence.
		    Serial.println();
		    Serial.println("Horizontal tab detected.");
		    terminateNow = true;
		}
		else {
		    Serial.print((char)c);          //Output each character to serial monitor for diagnostics.
		    barCode[charsRead] = (char)c;   //Add char to the end of the current barcode.
		}

		charsRead++;    //Increment the current barcode char index.
		totalChars++;   //Increment the total chars counter.
	}
	
	//Set a timeout period indcator after each new character is added to the barcode.
	if (lastCharIndex < charsRead) {
	    serBuf.readClear();         //Clear the serial buffer.
	    lastRead = millis();        //Set a timeout period if no end character was received.
	    lastCharIndex = charsRead;  //Update the lastCharIndex to the current index.
	    digitalWrite(LED, HIGH);    //Turn on the LED to indicate the timeout period is active.
	}
	
	//After the timeout, or a termination character is received, process the barcode.
	if ( (millis() - lastRead > readTimeout || terminateNow) && charsRead > 0 )  {
	    resetBarcode();
	}
    
}


//Processes a received barcode.
void resetBarcode() {
    digitalWrite(LED, LOW);     //Turn off the timeout period indicator LED.
    terminateNow = false;       //Reset the special char found bool.
    
    barCode[charsRead] = '\0';  //Terminate the char array with null terminator.
    totalBarcodes++;            //Increment the total barcodes counter.
    
    Serial.println();
    Serial.print("Full Barcode Received: ");
    Serial.println(barCode);
    
    //Publish the barcode to where it needs to go.
    Serial.println("Publishing to required endpoints...");
    //Send barcode to enpoints...
    //http to web API.
    sendToAPI(barCode);
    
    //Cloud publish.
    sendToCloud(barCode);
    
    //barCode = {""};
    
    charsRead = 0;      //Reset the current barcode char index to the start.
    lastCharIndex = charsRead;  //Reset the lastCharindex to the current index.
    
    Serial.println("Waiting for next barcode...");
    Serial.println();
}

void resetSequence() {
	Serial1.write(0);

	serBuf.readClear();

	// Keep reading for 1 second to make sure read buffer is cleared
	unsigned long startTime = millis();
	while(millis() - startTime < 1000) {
		serBuf.read();
	}
}

//Sends the ticket number to the API api.stdair.com/willcalltickets.
void sendToAPI(char *ticketNumber) {
    if (WiFi.ready()) {
        //Create the http request to the API endpoint.
        char deviceID[255];
        System.deviceID().toCharArray(deviceID,255);
        request.hostname = "api.stdair.com";
        request.port = 80;
        request.path = "/willcalltickets";
        sprintf(pubString, "{\"DeviceID\":\"%s\",\"PickTicketNo\":\"%s\"}", deviceID, ticketNumber);
        request.body = pubString;
    
        //Debugging output.
        char diagString[100];
        char hostName[50];
        char path[255];
        char body[255];
        request.hostname.toCharArray(hostName, 50);
        request.path.toCharArray(path,255);
        request.body.toCharArray(body,255);
        snprintf(diagString, sizeof(diagString), "Sending to %s\:%d%s", hostName, (char*)request.port, path);
        Serial.printlnf(diagString);
        snprintf(diagString, sizeof(diagString), "Request Body: %s", body);
        Serial.printlnf(diagString);
        
        //Send iit.
        http.post(request, response, headers);
        Serial.print("Application>\tResponse status: ");
        Serial.println(response.status);
    
        Serial.print("Application>\tHTTP Response Body: ");
        Serial.println(response.body);
    }
    else {
        Serial.println("WiFi not ready. Cannot send to API.");
    }
}

void sendToCloud(char *ticketNumber) {
    if (Particle.connected()) {
        publishQueue.publish("sal-barcode-scan",ticketNumber,60,PRIVATE);
    }
    else {
        Serial.println("Cloud not connected. Cannot publish to cloud.");
    }
}

Caveats:

  1. I’m not the best coder so use at your own risk!
  2. I did not find that reading terminating characters at the end of the barcode to be reliable enough so I added a “readTimeout”. If a character hasn’t been read within the timeout milliseconds, then the barcode is assumed to be terminated.
  3. I send the data to an HTTP endpoint which is on my local network controlled by me. The data is also non-sensitive. If you need security, look at webhooks instead.
  4. The baud rate of my barcode scanner is max 9600 baud. The SerialBufferRK library isn’t really necessary but it works nonetheless.
  5. I also use the PublishQueueAsyncRK library to manage the publish rate. Again, this might not be necessary since I doubt someone could generate enough barcode scans to exceed the acceptable cloud publish rate.
  6. I do not have the Photon serial TX (blue wire in photo) hooked up to the DB9 adapter. It was causing spurious character reads on the RX port.

Link to DB9 Serial to TTL converter:https://www.amazon.com/NulSom-Inc-Ultra-Compact-Converter/dp/B00OPU2QJ4/ref=pd_nav_b2b_ab_bia_t_1?_encoding=UTF8&psc=1&refRID=1G25P7EGY0EG3BA28QKB