How to detect USB cable disconnect in blocking read() call? - serial-port

I have a smart energy meter which sends energy consumption data every second. The daemon program I've written (C++/C Arch Linux) to read the data doesn't exit when the USB cable is disconnected and stalls indefinitely in the blocking read() call.
How to interrupt a blocking read() call (i.e. fail with EINTR instead of waiting for the next char)?
I extensively searched Google and looked here in SO and could not find an answer to this problem.
Details:
Smartmeter project source on Github
IR dongle with FT232RL USB to UART bridge
Datagrams have fixed length of 328 bytes sent every second
Read method detects beginning \ and end ! markers of a datagram
sigaction to catch CTRL+C SIGINT and SIGTERM signals
termios is setup to do blocking read() with VMIN = 1 and VTIME = 0.
Tried:
Playing with VMIN and VTIME
Removed SA_RESTART
Possible solution:
Use a non-blocking read method, maybe with select() and poll()
Or VMIN > 0 (datagram is longer than 255 characters and I would need to read the datagram in smaller chunks)
Not sure how to handle datagram begin/end detection and the one second interval between datagrams for a non-blocking read method
EDIT: The code below now buffers the read() call into an intermediate buffer of 255 bytes (VMIN = 255 and VTIME = 5) adapted from here. This avoids the small overhead of calling read() for every char. In practise this doesn't make a difference compared to reading one char at a time though. Read() still doesn't exit gracefully on cable disconnect. The daemon needs to be killed with kill -s SIGQUIT $PID. SIGKILL has no effect.
main.cpp:
volatile sig_atomic_t shutdown = false;
void sig_handler(int)
{
shutdown = true;
}
int main(int argc, char* argv[])
{
struct sigaction action;
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART;
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
while (shutdown == false)
{
if (!meter->Receive())
{
std::cout << meter->GetErrorMessage() << std::endl;
return EXIT_FAILURE;
}
}
Smartmeter.cpp:
bool Smartmeter::Receive(void)
{
memset(ReceiveBuffer, '\0', Smartmeter::ReceiveBufferSize);
if (!Serial->ReadBytes(ReceiveBuffer, Smartmeter::ReceiveBufferSize))
{
ErrorMessage = Serial->GetErrorMessage();
return false;
}
}
SmartMeterSerial.cpp:
#include <cstring>
#include <iostream>
#include <thread>
#include <unistd.h>
#include <termios.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include "SmartmeterSerial.h"
const unsigned char SmartmeterSerial::BufferSize = 255;
SmartmeterSerial::~SmartmeterSerial(void)
{
if (SerialPort > 0) {
close(SerialPort);
}
}
bool SmartmeterSerial::Begin(const std::string &device)
{
if (device.empty()) {
ErrorMessage = "Serial device argument empty";
return false;
}
if ((SerialPort = open(device.c_str(), (O_RDONLY | O_NOCTTY))) < 0)
{
ErrorMessage = std::string("Error opening serial device: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
if(!isatty(SerialPort))
{
ErrorMessage = std::string("Error: Device ") + device + " is not a tty.";
return false;
}
if (flock(SerialPort, LOCK_EX | LOCK_NB) < 0)
{
ErrorMessage = std::string("Error locking serial device: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
if (ioctl(SerialPort, TIOCEXCL) < 0)
{
ErrorMessage = std::string("Error setting exclusive access: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
struct termios serial_port_settings;
memset(&serial_port_settings, 0, sizeof(serial_port_settings));
if (tcgetattr(SerialPort, &serial_port_settings))
{
ErrorMessage = std::string("Error getting serial port attributes: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
cfmakeraw(&serial_port_settings);
// configure serial port
// speed: 9600 baud, data bits: 7, stop bits: 1, parity: even
cfsetispeed(&serial_port_settings, B9600);
cfsetospeed(&serial_port_settings, B9600);
serial_port_settings.c_cflag |= (CLOCAL | CREAD);
serial_port_settings.c_cflag &= ~CSIZE;
serial_port_settings.c_cflag |= (CS7 | PARENB);
// vmin: read() returns when x byte(s) are available
// vtime: wait for up to x * 0.1 second between characters
serial_port_settings.c_cc[VMIN] = SmartmeterSerial::BufferSize;
serial_port_settings.c_cc[VTIME] = 5;
if (tcsetattr(SerialPort, TCSANOW, &serial_port_settings))
{
ErrorMessage = std::string("Error setting serial port attributes: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
tcflush(SerialPort, TCIOFLUSH);
return true;
}
char SmartmeterSerial::GetByte(void)
{
static char buffer[SmartmeterSerial::BufferSize] = {0};
static char *p = buffer;
static int count = 0;
if ((p - buffer) >= count)
{
if ((count = read(SerialPort, buffer, SmartmeterSerial::BufferSize)) < 0)
{
// read() never fails with EINTR signal on cable disconnect
ErrorMessage = std::string("Read on serial device failed: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
p = buffer;
}
return *p++;
}
bool SmartmeterSerial::ReadBytes(char *buffer, const int &length)
{
int bytes_received = 0;
char *p = buffer;
bool message_begin = false;
tcflush(SerialPort, TCIOFLUSH);
while (bytes_received < length)
{
if ((*p = GetByte()) == '/')
{
message_begin = true;
}
if (message_begin)
{
++p;
++bytes_received;
}
}
if (*(p-3) != '!')
{
ErrorMessage = "Serial datagram stream not in sync.";
return false;
}
return true;
}
Many thanks for your help.

While the code below is not a solution to the original question on how to interrupt a blocking read() call, at least it would be a vialble workaround for me. With VMIN = 0 and VTIME = 0 this is now a non-blocking read():
bool SmartmeterSerial::ReadBytes(char *buffer, const int &length)
{
int bytes_received = 0;
char *p = buffer;
tcflush(SerialPort, TCIOFLUSH);
bool message_begin = false;
const int timeout = 10000;
int count = 0;
char byte;
while (bytes_received < length)
{
if ((byte = read(SerialPort, p, 1)) < 0)
{
ErrorMessage = std::string("Read on serial device failed: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
if (*p == '/')
{
message_begin = true;
}
if (message_begin && byte)
{
++p;
bytes_received += byte;
}
if (count > timeout)
{
ErrorMessage = "Read on serial device failed: Timeout";
return false;
}
++count;
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
if (*(p-3) != '!')
{
ErrorMessage = "Serial datagram stream not in sync.";
return false;
}
return true;
}
However, I am still curious to know if it is actually possible to interrupt a blocking `read()ยด as this workaround is constantly polling the serial port.
I believe reading one char at a time is not a problem as the received bytes from the UART are buffered by the OS - but constantly polling the buffer with read() is! Maybe I'll try a ioctl(SerialPort, FIONREAD, &bytes_available) before read(), although I don't know if this would actually make a difference.
Any suggestions?

Related

Arduino MEGA, LCD and SSR is frozen after 24 hours

I'm going to tell you my problem with arduino.
I am making an access rfdi system to enable a water pump, the procedure is as follows: I bring the rfid keyring closer to the reader, I make a sql query to the server and if it returns the data, I enable the pump and later register that user is served water.
Everything works fine for 18-24 hours, after that time the screen freezes and the arduino doesn't respond in any way or run the program. To get it to work again I have to restart it.
Maybe I thought it was a memory problem, but I don't know. On build I have Sketch (18%) and memory (22%).
I don't know where the problem is.
#include <U8glib.h>
#include <SPI.h>
#include <EthernetENC.h>
#include <Wiegand.h> //PROTOCOLO P/ RFDI
#include <ArduinoJson.h>
U8GLIB_ST7920_128X64_1X u8g(6, 5, 4 ,7); //LCD Enable, RW, RS, RESET
#define FALSE 0
#define TRUE 1
long int rawToken;
char c;
char pageAdd[64];
char user[64];
int totalCount = 0;
String ID = "";
String readString = "";
String clientegraba = "";
String terrenograba = "";
bool inicio = true;
bool errorbit = false;
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// CONFIG IP ARDUINO
IPAddress ip(192,168,0,210);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);
// IP Y PUERTO SERVIDOR
IPAddress server(192,168,0,104);
char serverName[] = "192.168.0.104";
int serverPort = 80;
EthernetClient client;
WIEGAND wg;
void setup() {
Serial.begin(9600);
pinMode(34,OUTPUT); //OUT PUMP
digitalWrite(34,LOW);
welcome(); //PANTALLA BIENVENIDA
//GATE A
wg.D0PinA =2;
wg.D1PinA =3;
//GATE B
wg.D0PinB =18;
wg.D1PinB =19;
//GATE C
wg.D0PinC =20;
wg.D1PinC =21;
// Reader enable
wg.begin(TRUE, FALSE, FALSE); // wg.begin(GateA , GateB, GateC)
// disable SD SPI
pinMode(4,OUTPUT);
digitalWrite(4,HIGH);
// Start ethernet
Serial.println(F("Iniciando ethernet..."));
Ethernet.begin(mac, ip, gateway, gateway, subnet);
Serial.println(Ethernet.localIP());
delay(2000);
Serial.println(F("Listo"));
ingresellave();
}
void loop()
{
if(wg.available()){
errorbit = false;
espere(); //WAITING SCREEN
ID = String(wg.getCode()); //OBTIENE ID DE TARJETA
String data = "/test.php?cliente=" + ID; //CONCAT ID
sprintf(pageAdd,data.c_str(),totalCount);
Serial.println(pageAdd);
if(!getPage(server,serverPort,pageAdd)){
Serial.print(F("Falla "));
}else{
Serial.print(F("Paso "));
}
totalCount++;
Serial.println(totalCount,DEC);
}
if(ID != ""){
if(clientegraba != ""){
grabacarga(clientegraba,terrenograba);
}else{
if(errorbit == false){
noexiste();
delay(3000);
ID = "";
ingresellave();
}
}
}
delay(200);
}
byte getPage(IPAddress ipBuf,int thisPort, char *page)
{
int inChar;
char outBuf[128];
Serial.print(F("Conectando..."));
if(client.connect(ipBuf,thisPort) == 1){
Serial.println(F("Conectado"));
sprintf(outBuf,"GET %s HTTP/1.1",page);
client.println(outBuf);
sprintf(outBuf,"Host: %s",serverName);
client.println(outBuf);
client.println(F("Connection: close\r\n"));
}else{
error();
errorbit = true;
Serial.println(F("Error en servidor"));
return 0;
}
// connectLoop controls the hardware fail timeout
int connectLoop = 0;
boolean reader = false;
while(client.connected())
{
while(client.available())
{
String line = client.readStringUntil('\n');
if(reader){
StaticJsonBuffer<200> jsonBuffer;
JsonObject& datos = jsonBuffer.parseObject(line);
String apellido = datos["name"];
String cliente = datos["cliente"];
clientegraba = cliente;
long total = datos["total"];
long libres = datos["libres"];
String terreno = datos["terreno"];
terrenograba = terreno;
String costo = datos["costo"];
float price = costo.toFloat();
usuario(apellido,total,libres,price);
Serial.println("User: " + apellido);
Serial.println("Cliente: " + cliente);
Serial.println("Tarjeta: " + ID);
Serial.println("Terreno: " + terreno);
Serial.println("Costo: " + String(costo));
Serial.println("Total: " + String(total));
Serial.println("Libres: " + String(libres));
if(apellido != ""){
digitalWrite(34,HIGH); //PUMP START
delay(1000);
digitalWrite(34,LOW); //PUMP STOP
}
}
if(line == "\r") {
reader = true;
break;
}
// set connectLoop to zero if a packet arrives
connectLoop = 0;
}
connectLoop++;
// if more than 10000 milliseconds since the last packet
if(connectLoop > 10000)
{
// then close the connection from this end.
Serial.println();
Serial.println(F("Timeout"));
client.stop();
}
// this is a delay for the connectLoop timing
delay(1);
}
Serial.println();
Serial.println(F("Desconectando."));
// close client end
client.stop();
delay(100);
return 1;
}
void grabacarga(String cliente, String terreno){
String data = "/graba.php?cliente=" + cliente + "&tarjeta=" + ID + "&terreno='" + terreno + "'";
sprintf(pageAdd,data.c_str(),totalCount);
Serial.println(pageAdd);
if(!getPage(server,serverPort,pageAdd)) Serial.print(F("Falla "));
else Serial.print(F("Paso "));
totalCount++;
Serial.println(totalCount,DEC);
ID = "";
terrenograba = "";
clientegraba = "";
inicio = true;
delay(9000);
ingresellave();
}
void welcome() {
u8g.setRot180();
u8g.setFont(u8g_font_unifont);
u8g.firstPage();
do {
u8g.drawStr(18,22,"SISTEMA");
u8g.drawStr(15,50,"ACCESO RFID");
} while( u8g.nextPage() );
}
void ingresellave() {
u8g.setFont(u8g_font_unifont);
u8g.firstPage();
do {
u8g.drawStr(0,10,"COLOQUE EL BIDON");
u8g.drawStr(2,35,"Y LUEGO INGRESE");
u8g.drawStr(25,60,"UNA LLAVE");
} while( u8g.nextPage() );
}
void error() {
u8g.setFont(u8g_font_unifont);
u8g.firstPage();
do {
u8g.drawStr(40,10,"ERROR");
u8g.drawStr(33,35,"INTENTE");
u8g.drawStr(22,60,"NUEVAMENTE");
} while( u8g.nextPage() );
}
void espere() {
u8g.setFont(u8g_font_unifont);
u8g.firstPage();
do {
u8g.drawStr(25,35,"ESPERE...");
} while( u8g.nextPage() );
}
void usuario(String user, long total, long libres, float costo) {
u8g.firstPage();
do {
u8g.setFont(u8g_font_6x10);
u8g.drawStr(0,10,user.c_str());
String carga = "Carga:" + String(total) + "/" + String(libres);
u8g.setFont(u8g_font_unifont);
u8g.drawStr(15,33,carga.c_str());
if(total > libres){
long precio = total - libres;
float costototal = precio * costo;
Serial.println(costototal);
String valor = "Costo:$" + String(costototal);
u8g.drawStr(0,60,valor.c_str());
}
} while( u8g.nextPage() );
}
void noexiste(){
u8g.setFont(u8g_font_unifont);
u8g.firstPage();
do {
u8g.drawStr(34,22,"USUARIO");
u8g.drawStr(12,50,"NO REGISTRADO");
} while( u8g.nextPage() );
}
The system power is from a 12V 4.5A source and the Arduino Mega along with the screen(LCD12864A) are powered from the 5V pin through an external LM2596 regulator.
The screen data pins are connected to pins 4,5,6 and 7, the rfid reader to pins 2 and 3 and the water pump output is pin 34 which has a 2222a pnp transistor connected with a 1K base resistor, emitter to ground and collector to an SSR-24DA solid state relay.
Thank you.
After reading your comment mentioning wiegand malfunction, and the fact that it only malfunctioned after several hours of running well, I would try and see if it's overheating somehow, or try and reduce EMI by using EMI filter on the power supply and shielding the circuit board with aluminium box or the like.
Water pump can cause high EMI and voltage spike in the mains upon turning on or off.

Arduino ESP8266 EEPROM commit() returns false

I want to save the SSID and password in the EEPROM in my Arduino Sketch Wlan.
Actually everything works so far, except that the commit () returns a false. And that's why the memory is empty again after a restart.
My code:
void writePROM()
{
EEPROM.begin(0);
EEPROM.write(0, 0xAA);
byte ssidLength = ssid.length();
byte passLength = pass.length();
int adress = 2;
EEPROM.write(1, (byte)ssidLength);
for(int i = 0; i < ssidLength; i++)
{
EEPROM.write(adress + i, (byte)ssid[i]);
}
adress += ssidLength + 1;
EEPROM.write(adress++, passLength);
for(int i = 0; i < passLength; i++)
{
EEPROM.write(adress + i, pass[i]);
}
bool bRc = EEPROM.commit();
if(bRc)
{
Serial.println("Write successfully");
}
else
{
Serial.println("Write error");
}
Serial.println("Write name to EEPROM = " + ssid);
Serial.println("Write password to EEPROM = " + pass);
}
What I am doing wrong?
First of all, good on you for checking the result of the commit() call.
You're passing 0 when you initialize the EEPROM library:
EEPROM.begin(0);
You need to pass it the number of bytes that you're looking to store using it.
You can read the code for the EEPROM library to confirm this:
void EEPROMClass::begin(size_t size) {
if (size <= 0) {
DEBUGV("EEPROMClass::begin error, size == 0\n");
return;
}
If you pass 0 it simply returns without doing any setup. You can also see that commit() will do nothing in this case:
bool EEPROMClass::commit() {
if (!_size)
return false;
In your case you should call EEPROM.begin() with at least the maximum size of an SSID and password plus one each for the zero terminating bytes (so, 32 characters for the SSID, 63 for the password, plus 2 for 97).
But, as #Juraj pointed out in the comments, you don't need to do any of this as the ESP8266 will automatically persist the Wifi credentials.

ESP8266 - Response from server gets cut

I'm using an ESP8266 connected to an Arduino one via SoftwareSerial to make a post request to a node web server. The ESP8266 sends some data to the server and it should get back other data. The data arrives at the server correctly, but the response from the server is incomplete (it gets cut each time in a different way) and I can't access the body of the response from my Arduino sketch. The server sends the response correctly, as i've checked with hurl.
This is my code:
#include "SoftwareSerial.h"
String ssid ="ssid";
String password="pwd";
SoftwareSerial esp(3, 2);// RX, TX
ESP8266_Simple wifi(3,2);
String data;
String server = "server";
String uri = "uri";
String token = "token";
float temp_set = 15; //standard values
float temp_rec = 15;
String temp_set_s;
String temp_rec_s;
int activate = LED_BUILTIN; //pin for relay
int button_up = 4;
int button_down = 5;
unsigned long time;
//LCD
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
// DHT11
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
#define DHTPIN 6
#define DHTTYPE DHT22
DHT_Unified dht(DHTPIN, DHTTYPE);
void setup() {
esp.begin(9600);
Serial.begin(9600);
delay(10);
reset();
connectWifi();
pinMode(activate, OUTPUT);
pinMode(button_up, INPUT);
pinMode(button_down, INPUT);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
//DHT setup
dht.begin();
sensor_t sensor;
delay(500);
}
//reset the esp8266 module
void reset() {
esp.println("AT+RST");
delay(1000);
if(esp.find("OK") ) Serial.println("Module Reset");
}
//connect to your wifi network
void connectWifi() {
String cmd = "AT+CWJAP=\"" +ssid+"\",\"" + password + "\"";
esp.println(cmd);
delay(4000);
if(esp.find("OK")) {
Serial.println("Connected!");
time = millis();
} else {
connectWifi();
Serial.println("Cannot connect to wifi");
}
}
void loop () {
//temp_rec_s = String(temp_rec);
//temp_set_s = String(temp_set);
//data = "tempRec=" + temp_rec_s + "&tempSet=" + temp_set_s;
//httppost();
// dht data
sensors_event_t event;
dht.temperature().getEvent(&event);
temp_rec = event.temperature;
//temp_rec_s = String(temp_rec);
//temp_set_s = String(temp_set);
//data = "tempRec=" + temp_rec_s + "&tempSet" + temp_set_s;
// to activate
if(temp_set < temp_rec){
digitalWrite(activate, LOW);
} else{
digitalWrite(activate, HIGH);
}
//function for physical buttons
if((digitalRead(button_up)) == HIGH){
temp_set = temp_set + 0.5;
delay(100);
}
if((digitalRead(button_down)) == HIGH){
temp_set = temp_set - 0.5;
delay(100);
}
//shows temperature on display
lcd.setCursor(0, 0);
lcd.print("T rec " + String(temp_rec));
//shows temperature on display
lcd.setCursor(0, 1);
lcd.print("T set " + String(temp_set));
temp_rec_s = String(temp_rec);
temp_set_s = String(temp_set);
data = "tempRec=" + temp_rec_s + "&tempSet=" + temp_set_s + "&token=" + token;
//Serial.println(data);
if((millis() - time) >= 10000){
httppost();
}
delay(200);
}
void httppost () {
esp.println("AT+CIPSTART=\"TCP\",\"" + server + "\",80");//start a TCP connection.
if(esp.find("OK")) {
Serial.println("TCP connection ready");
}
delay(1000);
String postRequest =
"POST " + uri + " HTTP/1.0\r\n" +
"Host: " + server + "\r\n" +
"Accept: *" + "/" + "*\r\n" +
"Content-Length: " + data.length() + "\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n" +
"\r\n" + data;
String sendCmd = "AT+CIPSEND="; //determine the number of caracters to be sent.
esp.print(sendCmd);
esp.println(postRequest.length());
Serial.println(postRequest);
delay(500);
if(esp.find(">")) {
Serial.println("Sending..");
esp.print(postRequest);
String tmpResp = esp.readString();
Serial.println(tmpResp);
if(esp.find("SEND OK")) {
Serial.println("Packet sent");
while(esp.available()) {
String line = esp.readString();
Serial.print(line);
}
// close the connection
esp.println("AT+CIPCLOSE");
}
}
}
Put a delay(1) under the esp.readString() and use .read() instead with char like this:
while(esp.available())
{
char line = esp.read(); // read one char at a time
delay(1); // prevent freezing
Serial.print(line);
if (line == '\0') continue; // terminate the `while` when end of the data
}
The .readString() method as pointed out by #gre_gor reads until there is no incoming data for 1 second.
So the better method is to use read() and char since you can test the char to see if you have reached the end of data character \0.
When using .read() consider using a custom timeout, because data can be delivered with delays so you might want to keep trying for a certain period of time if you haven't yet reached the end of data character \0, like this:
long int time = millis(); // current time
long int wait = 1000 * 10; // wait 10 seconds before terminating the read process
while ((time + wait) > millis())
{
while (esp.available())
{
char line = esp.read();
delay(1);
Serial.print(line);
if (line == '\0') continue;
}
}

Arduino 1 not reading correctly the string from Serial Monitor

I am having a problem with the serial monitor on Arduino Uno.
Basically I want to write some commands on the Serial Monitor, read the string and according to the string do something.
The problem is the following: supposing I type the command 'read 4' in the Serial Monitor, sometimes the string is read correctly, sometimes it is read like: 'ead 4', missing the first character.
I even put a delay between two readings from the Serial Monitor. Does anyone have an explanation?
For completeness I post my code (basically it reads/writes from/to the EEPROM: for example 'read 5' will read the 5 block of EEPROM, 'write 4 5' will write the value 5 to the 4th block of memory).
#define MAX_STRING_LENGTH 14
#include <ctype.h>
#include <EEPROM.h>
//The function initializes the string to spaces
void initString(char* mystr, char strLength);
//The function returns true if it is a read operation, false otherwise
boolean isReadEEPROM(char *myStr, char strLength);
//The function returns true if it is a write operation, false otherwise
boolean isWriteEEPROM(char *myStr, char strLength);
//The function returns the EEPROM address from the string
unsigned int findAddress(char *myStr, char strLength);
char findValue(char *myStr, char strLength);
//Check the address range
boolean isAddressOk(unsigned int address);
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
char pos = 0;
bool newDataFound = false;
char serialStr[MAX_STRING_LENGTH];
unsigned int address = 0;
char val = 0;
while(Serial.available()){
val = Serial.read();
}
val = 0;
initString(&serialStr[0], (char) MAX_STRING_LENGTH);
while(Serial.available() && pos < MAX_STRING_LENGTH){
serialStr[pos] = Serial.read();
pos ++;
newDataFound = true;
delay(200);
}
if (newDataFound){
Serial.print("New Command found: ");
Serial.println(serialStr);
address = 0;
address = findAddress(&serialStr[0], MAX_STRING_LENGTH);
if (isReadEEPROM(&serialStr[0], MAX_STRING_LENGTH) && isAddressOk(address)){
Serial.println("Reading from EEPROM");
Serial.print("Address is ");
Serial.println(address);
val = EEPROM.read(address);
Serial.print("Value is: ");
Serial.println( (uint8_t) val );
Serial.println(" ");
}
else if (isWriteEEPROM(&serialStr[0], MAX_STRING_LENGTH) && isAddressOk(address)){
Serial.println("Writing to EEPROM");
Serial.print("Address is ");
Serial.println(address);
Serial.println(" ");
val = findValue(&serialStr[0], MAX_STRING_LENGTH);
EEPROM.write(address, val);
}
else{
if (!isAddressOk(address)){
Serial.write(address);
Serial.println("Address out of range");
Serial.println("");
}
Serial.println("Not recognized operation\n");
}
delay(2000);
}
}
void initString(char* mystr, char strLength){
for(char ii=0; ii<strLength; ii++){
(*mystr) = ' ';
mystr++;
}
}
//The function returns true if it is a read operation, false otherwise
boolean isReadEEPROM(char *myStr, char strLength){
//The string should contain first the 'read' operation
char expected[] = "read";
int ii =0;
while (ii<4){
if ( *(myStr + ii) != expected[ii]){
return false;
Serial.println("Not a Read Operation\n");
}
ii++;
}
return true;
Serial.println("Read Operation");
}
//The function returns true if it is a write operation, false otherwise
boolean isWriteEEPROM(char *myStr, char strLength){
//The string should contain first the 'read' operation
char expected[] = "write";
int ii =0;
while (ii<5){
if ( *(myStr + ii) != expected[ii]){
return false;
}
ii++;
}
return true;
}
//The function returns the EEPROM address from the string
unsigned int findAddress(char *myStr, char strLength){
unsigned int address;
char tmpStr[strLength];
char strAddress[] = " ";
int ii = 0;
while(ii< strLength){
tmpStr[ii] = *(myStr+ii);
ii++;
}
Serial.print("The address found is: ");
Serial.println(strAddress);
ii= 0;
if (isReadEEPROM(myStr, strLength)){
while (ii<=4){
if (isdigit(*(myStr + 5 + ii))){
strAddress[ii] = *(myStr + 5 + ii);
}
else{
break;
}
ii++;
}
address = atoi(strAddress);
}
else if(isWriteEEPROM(myStr, strLength)){
while (ii<=4){
if (isdigit(*(myStr + 6 + ii))){
strAddress[ii] = *(myStr + 6 + ii);
}
else{
break;
}
ii++;
}
address = atoi(strAddress);
}
else{
address = 0;
//Serial.println("Address not available in function 'findAddress'");
}
return address;
}
//The function returns the value to be written to the EEPROM from the string
char findValue(char *myStr, char strLength){
char val;
char tmpStr[strLength];
char strVal[] = " ";
int ii, idx = 0;
while(ii< strLength){
tmpStr[ii] = *(myStr+ii);
ii++;
}
ii= 0;
// first found the first digits corresponding to the address
while (ii<=4){
if (isdigit(*(myStr + 6 + ii))){
;//strAddress[ii] = *(myStr + 6 + ii);
}
else{
ii++;
break;
}
ii++;
}
// now find the value
while (ii<=4+3){
Serial.println(*(myStr + 6 + ii));
if (isdigit(*(myStr + 6 + ii))){
strVal[idx] = *(myStr + 6 + ii);
}
else{
break;
}
ii++;
idx++;
}
Serial.print("original string: ");
Serial.println(tmpStr);
Serial.print("Value found: ");
Serial.println(strVal);
val = (char)atoi(strVal);
return val;
}
boolean isAddressOk(unsigned int address){
if (address < 1024 && address >= 0){
return true;
}
else{
return false;
}
}
This snippet:
char val=0;
while(Serial.available()){
val = Serial.read();
}
val = 0;
Is just consuming any characters that may be left in the input buffer. You could also do:
while (Serial.avaialble())
Serial.read();
The next while loop does not wait for the entire command. Sometimes, it gets the 'r', and then doesn't get the 'ead...' in time. They will be there the next time loop executes, so it looks like the 'r' is missing. It was just consumed in the previous loop.
Things sent over the USB (from the Serial Monitor window) can have odd delays in them.
To gather up a complete line, you should save characters until a '\n' is received:
for (;;) {
if (Serial.available()) {
char c = Serial.read();
if (c == '\n')
break;
if (pos < MAX_LINE_LENGTH) {
serialStr[pos] = c;
pos ++;
}
newDataFound = true;
}
}
The delay call is totally unnecessary, because the for loop waits until a '\n' character is received (be sure that in the Serial Monitor pull down menu either 'New Line' or 'both NL & CR' is selected). Then you know you've read all the characters in the line.

Unix Client and Server Program - Very Odd Client and Server Socket Bug

I am currently making a client and server program in the Unix Environment. I have made it so the client is able to upload the contents of a file to the client. I am now in the process of adding options and error handlers to the server example the client must enter the file name. To do this i wanted to send a message saying OK if all the options checked out however if i do this it seems to cause my file reading and sending to go Crazy and I have no idea why.
I have uploaded the functions for doing this
Client Code
int putFile (char path[256], int fd)
{
char mystring[1000];
char buffer[100];
int i , n;
FILE * pFile;
n = read(fd,buffer,100);
printf("%s", buffer);
if (strcmp(buffer, "OK") == 0)
{
pFile = fopen(path, "r");
if(pFile != NULL)
{
while(fgets(mystring, sizeof(mystring), pFile) != NULL)
{
//fputs(mystring, fd);
write(fd,mystring,strlen(mystring));
}
}
else
{
printf("Invalid File or Address \n");
}
fclose(pFile);
}
else
{
printf("%s \n", buffer);
}
}
Server Code for reading the socket
int putRequest(int fd, char buf[], char str[])
{
char data[256];
int number;
char * ptr;
char results[100];
int total = 0;
char *arguments[1024];
char temp[10];
int i;
ptr = strtok(buf," ");
while (ptr != NULL)
{
char * temp;
temp = (char *)malloc(sizeof(ptr));
temp = ptr;
arguments[total] = temp;
total++;
ptr = strtok (NULL, " ");
}
if(total == 1)
{
strcat(str, "Invaild Arguments \n");
return 1;
}
write(fd, "OK", 256);
FILE * pFile;
pFile = fopen ("myfile.txt","w");
if (pFile!=NULL)
{
while(read(fd, data, 256) != NULL)
{
fputs(data, pFile);
}
fclose (pFile);
}
else
{
strcat(str, "Invaild File");
return 0;
}
strcat(str, "Done");
return 1;
}
Thanks in advance and just post something if you need to see more code. I just placed the code which should be causing the problem.

Resources