Arduino read big amount of serial data without losses - arduino

In Arduino Serial I need to read those kind of lines <1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16> As fast
as possible, without blocking.
The goal is to set values of array compensation[] without delay.
In this example <1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16> Would display 1000 (as 100% have been read) But I don't get 100% but periodique losses at 96% and then 95% … So I need my code to display 1000 (for 100%) every time.
This is what I have already :
static int compensastion[64];
int passage=1;
const byte numChars = 1000;
char receivedChars[numChars];
boolean newData = false;
int score = 0;
int mal_score = 0;
void setup() {
Serial.begin(115200);
Serial.println();
}
void loop() {
read_inputs();
// get the percentage of success for this simple message from 1 to 16
Serial.print(double(double(score) / double(mal_score+score))*10000);
Serial.println();
}
void read_inputs(){
recvWithStartEndMarkers();
check();
}
// Get the full line <1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16>
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
if (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0';
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
// Parse the line in digits
void check(){
if (newData == true) {
newData = false;
int marge = 0;
passage=1;
for(int j = 0; j < numChars; j=j+1){
int my_int = -1;
char buf[4];
if(isDigit(receivedChars[j]) &&marge == 0 ){
if(!isDigit(receivedChars[j+1])){
my_int = receivedChars[j] - '0';
marge=marge+1;
}
if(isDigit(receivedChars[j+1]) && !isDigit(receivedChars[j+2]) ){
my_int = receivedChars[j] - '0';
my_int = my_int*10 + (receivedChars[j+1] - '0');
marge=marge+2;
}
if(isDigit(receivedChars[j+1]) && isDigit(receivedChars[j+2]) && !isDigit(receivedChars[j+3]) ){
my_int = receivedChars[j] - '0';
my_int = my_int*10 + (receivedChars[j+1] - '0');
my_int = my_int*10 + (receivedChars[j+2] - '0');
marge=marge+3;
}
}
if (isDigit(receivedChars[j]) && my_int != -1){
if(passage == my_int){
score=score+1;
}else{
mal_score= mal_score+1;
}
compensastion[(passage-1)*2] = my_int;
passage=passage+1;
if(passage+1 >= 16+2){
passage=1;
}
}
if(marge > 0){
marge=marge-1;
}
}
}
}

In my experience, one of the most effective ways to handle huge amounts of serial data is using Interrupts and Finite State Machines (FSM).
Please carefully read this guide by the amazing Nick Gammon. FSMs are the most versatile approach when dealing with intensive processing tasks. (Not to be confused with RTOS, though)
Regarding the serial communication, I would implement the serial reading through the serialEvent() interrupt along the lines of the following example:
char serialString[] = " "; // Empty serial string variable
bool stringFinished = false; // Flag to indicate reception of a string after terminator is reached
void setup(){
previousEncoderTime = 0;
}
void loop(){
unsigned long now = millis();
if (stringFinished){ // When the serial Port has received a command
stringFinished = false;
// Implement your logic here
}
}
void serialEvent()
{
int idx = 0;
while (Serial.available())
{
char inChar = (char)Serial.read();
if (inChar == '\n') // The reading event stops at a new line character
{
serialTail = true;
serialString[idx] = inChar;
}
if (!serialTail)
{
serialString[idx] = inChar;
idx++;
}
if (serialTail)
{
stringFinished = true;
Serial.flush();
serialTail = false;
}
}
}
Finally, try to compartmentalize all the necessary data processing in single-purpose functions. This way your code will have a proper flow and you will reduce the risk of blocking the system with heavy tasks.
EDIT: I forgot to mention that the Serial.available() is regarded as a polling method which is highly unreliable and time-consuming. Stick to interrupts and you won't have massive issues

Related

Arduino / DigiSpark / ATtiny85 - Receiving and parsing several pieces of data

i'm trying to make a digispark to work with bluetooth and NeoPixel, while i had success into making it go on and off i would like to expand on it.
I have used the Robin2 example 5 method to reciece, and parse my data in order to get the leds color the way i want.
https://forum.arduino.cc/index.php?topic=396450.msg2727728#msg2727728
this is the code in my case - the original is in the link above
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
strip.setBrightness(xB);
controlLed();
newData = false;
}
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (bluetooth.available() > 0 && newData == false) {
rc = bluetooth.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
} else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
} else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void parseData() { // split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars, ","); // get the first part - the string
eFx = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ","); // get the first part - the string
rC1 = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
gC1 = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
bC1 = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ",");
xS = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ",");
xB = atoi(strtokIndx); // convert this part to an integer
}
void controlLed() {
colorWipe(strip.Color(rC1, gC1, bC1), xS);
colorWipe(strip.Color(20, 50, 100), 50);
}
now i have some issues, as the ATtiny85 memory is not that great i ran into issues when i try to send longer strings, basically i want to send all these:
int eFx = 1;
int rC1 = 100;
int gC1 = 100;
int bC1 = 100;
int xS = 20;
int xB = 100;
int rC2 = 5;
int gC2 = 50;
int bC2 = 200;
but when i try to parse it with the strtokIndx method above the digispark stops working (when i add more to what is already shown in the code ex above) i guess it's due to memory issues.
i understand part of the code,but not well enough to expand it in the direction that i want.
Right now the way it works is that when i send via BTooth info in this format asd<1,24,23,54,...,>qwe the recvWithStartEndMarkers () will grab all there is between < > and store it (?) in receivedChars
then i'm not sure why and how parseData() grabs this string and not something else, is it because of the function in the loop?
strcpy(tempChars, receivedChars);
parseData();
Well, i want to try to make this process go at it again, to recieve another string under [ ] and not < > and parseData2() (maybe) on receivedChars2 to get my last 3 INTs (color values for NeoPixel) parsed and stored in order to use them without having the digispark crash out of memory
The first step would be to change the
void recvWithStartEndMarkers() {
to detect also de diffrence between < and [ and run the proper code in order to store either in receivedChars or receivedChars2
not sure how to do that, also i'm not sure if this would be useful and would solve my memory issues.
any thoughts?
here is the full code i'm running
#include <Adafruit_NeoPixel.h> // NeoPixel Lib
#include <SoftSerial.h> // Serial Lib
#define LED_PIN 2
#define LED_COUNT 30
SoftSerial bluetooth(4, 5); // RX TX
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
const byte numChars = 42;
char receivedChars[numChars];
char tempChars[numChars];
boolean newData = false;
int eFx = 1;
int rC1 = 100;
int gC1 = 100;
int bC1 = 100;
int xS = 20;
int xB = 100;
int rC2 = 5;
int gC2 = 50;
int bC2 = 200;
void setup() {
bluetooth.begin (9600);
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
}
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
strip.setBrightness(xB);
controlLed();
newData = false;
}
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (bluetooth.available() > 0 && newData == false) {
rc = bluetooth.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
} else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
} else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void parseData() { // split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars, ","); // get the first part - the string
eFx = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ","); // get the first part - the string
rC1 = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
gC1 = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
bC1 = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ",");
xS = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ",");
xB = atoi(strtokIndx); // convert this part to an integer
}
void controlLed() {
colorWipe(strip.Color(rC1, gC1, bC1), xS);
colorWipe(strip.Color(20, 50, 100), 50);
}
void controlLed2() {
colorWipe(strip.Color(rC2, gC2, bC2), xS);
}
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}

Arduino Uno low memory available

i'm trying to do a communication over ethernet using ENC28J60 and Arduino Uno and run 3 task . I have a little problem with my arduino code. My code is compiling but i get the following error : "Low memory available, stability problems may occur." and the led on the board is blinking very fast. I guess that the board is trying to alocate memory but he faild. Any idea what can i do ?
#include <Arduino_FreeRTOS.h>
#include "HX711.h"
#include <PID_v1.h>
#include <string.h>
//#include <SPI.h>
#include <UIPEthernet.h>
#define configUSE_IDLE_HOOK 0
// FreeRTOS tasks
void TaskPrimaryControlLoop(void *pvParameters);
void TaskConcentrationControlLoop(void *pvParameters);
void TaskIdle(void *pvParameters);
/* Weigth Cells */
#define hx_pf_dout 3
#define hx_pf_clk 2
#define hx_c_dout 5
#define hx_c_clk 4
HX711 pf_scale(hx_pf_dout, hx_pf_clk);
HX711 c_scale(hx_c_dout, hx_c_clk);
float pf_factor = -236000;
float c_factor = -203000;
float p_weigth = 0; // (kg, 0.000) primary liquid weigth
float p_l_weigth = 0; // (kg, 0.000) primary liquid last weigth
float c_weigth = 0; // (kg, 0.000) concentrate liquid weigth
float c_l_weight = 0; // (kg, 0.000) concentrate liquid last weigth
/* h bridge config */
#define speed_p 9
#define forward_p 7
#define backward_p 8
#define speed_c 6
#define forward_c A0
#define backward_c A1
double p_pump = 0; // 0-255 pwm pump output
double c_pump = 0; // 0-255 pwm pump output
//// PID parameters
// Primary Control Loop
#define p_kp 250.0
#define p_ki 25.0
// Concentration Control Loop
#define c_kp 250.0
#define c_ki 25.0
double p_pv = 0; // (%) primary flow value
double c_pv = 0; // (%) concentration flow value
double p_sp = 0; // (l/min) primary flow setpoint
double c_sp_proc = 0; // % concentration
double c_sp = 0; // (l/min) concentration setpoint
PID pid_pcl(&p_pv, &p_pump, &p_sp, p_kp,p_ki,0.0, DIRECT);
PID pid_ccl(&c_pv, &c_pump, &c_sp, c_kp,c_ki,0.0, DIRECT);
/* Communication Ethernet */
#define MAX_STRING_LEN 32
const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 2);
IPAddress myDns(192,168,1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
// telnet defaults to port 23
EthernetServer server(23);
//EthernetClient client;
boolean alreadyConnected = false; // whether or not the client was connected previously
void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB, on LEONARDO, MICRO, YUN, and other 32u4 based boards.
}
pinMode(forward_p, OUTPUT);
pinMode(backward_p, OUTPUT);
pinMode(forward_c, OUTPUT);
pinMode(backward_c, OUTPUT);
pinMode(speed_p, OUTPUT);
pinMode(speed_c, OUTPUT);
digitalWrite(backward_p, HIGH);
digitalWrite(backward_c, HIGH);
digitalWrite(forward_p, LOW);
digitalWrite(forward_c, LOW);
pf_scale.set_scale(pf_factor);
c_scale.set_scale(c_factor);
//pf_scale.tare();
//c_scale.tare();
pid_ccl.SetMode(AUTOMATIC);
pid_pcl.SetMode(AUTOMATIC);
xTaskCreate(
TaskPrimaryControlLoop
, (const portCHAR *)"PrimaryControlLoop" // A name just for humans
, 128 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL );
xTaskCreate(
TaskConcentrationControlLoop
, (const portCHAR *)"ConcentrationControlLoop" // A name just for humans
, 128 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL );
xTaskCreate(
TaskIdle
, (const portCHAR *)"Idle" // A name just for humans
, 512 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 0 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL );
}
void loop() {
}
void TaskPrimaryControlLoop(void *pvParameters)
{
//(void) pvParameters;
for (;;)
{
p_weigth = pf_scale.get_units(1);
p_pv = (p_l_weigth - p_weigth)*100;
if(p_pv < 0) p_pv = 0;
pid_pcl.Compute();
analogWrite(speed_p, p_pump);
p_l_weigth = p_weigth;
vTaskDelay(200 / portTICK_PERIOD_MS); // 200 ms sample time
}
}
void TaskConcentrationControlLoop(void *pvParameters)
{
//(void) pvParameters;
for (;;)
{
c_weigth = c_scale.get_units(1);
c_pv = (c_l_weight - c_weigth)*100;
if(c_pv < 0) c_pv = 0;
c_sp = p_sp * (c_sp_proc/100);
pid_ccl.Compute();
analogWrite(speed_c, c_pump);
c_l_weight = c_weigth;
vTaskDelay(200 / portTICK_PERIOD_MS); // 200 ms sample time
}
}
void TaskIdle(void *pvParameters)
{
//(void) pvParameters;
for(;;){
EthernetClient client = server.available();
// when the client sends the first byte, say hello:
if (client) {
if (!alreadyConnected) {
client.flush();
//Serial.println("We have a new client");
//client.println("Hello, client!");
alreadyConnected = true;
}
}
recvWithStartEndMarkers();
//showNewData();
if(receivedChars[0] == 's'){
p_sp = atof(subStr(receivedChars, ",", 2));
c_sp_proc = int(subStr(receivedChars, ",", 3));
newData = false;
memset(receivedChars, 0, sizeof(receivedChars));
}
// send process values to application
if(receivedChars[0] == 'w'){
Serial.print(p_pv);
Serial.print(",");
Serial.print(p_sp);
Serial.print(",");
Serial.print(int(c_pump));
Serial.print(",");
Serial.print(c_pv);
Serial.print(",");
Serial.print(c_sp);
Serial.print(",");
Serial.print(int(p_pump));
Serial.println();
newData = false;
memset(receivedChars, 0, sizeof(receivedChars));
}
/*
// check commands
while(Serial.available() > 7){
p_sp = Serial.parseFloat();
c_sp_proc = Serial.parseInt();
}
newData = false;
memset(receivedChars, 0, sizeof(receivedChars)
*/
newData = false;
memset(receivedChars, 0, sizeof(receivedChars));
vTaskDelay(1);
}
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
EthernetClient client = server.available();
while (client.available() > 0 && newData == false) {
rc = client.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// Function to return a substring defined by a delimiter at an index
char* subStr (char* str, char *delim, int index) {
char *act, *sub, *ptr;
static char copy[MAX_STRING_LEN];
int i;
// Since strtok consumes the first arg, make a copy
strcpy(copy, str);
for (i = 1, act = copy; i <= index; i++, act = NULL) {
//Serial.print(".");
sub = strtok_r(act, delim, &ptr);
if (sub == NULL) break;
}
return sub;
}
First thing to do is try to increase the stack depth of your Tasks.
You're currently using 128, 128 and 512.
You can use "StackHighWaterMark" to get the info about the amount of stack space remaining. Try to use this information to "calibrate" the depth of your task. I recommend using at least 40% of free space.
In order to save space in the Arduino memory you can put all the Serial.print in the flash memory, try to use this syntax:
Serial.print(F(","));
Basically you have to add an F in front of the string that you want to print, but you can't do that when you print a variable:
Serial.print(int(c_pump)); // you can't do that here

Incremental index doesn't change at end of for loop

I want to interface Arduino with PLC to pull some information.
My problem is at Function 3: Set alarm flag / reset flag. This function is used to compare history value and present value. I tried to process some integer number (test_number) and process like binary 16 bits data for finding 1 at some bit. I found the for loop in Findbit function, which should repeat 16 times, runs infinitely. It does not change the incremental index (variable name bit_1) which is still stuck at 1.
This is my code :
int test_number_array[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int test_number = 0;
int bit_1 = 0;
int Andbit = 0;
const char* message;
int flagAlarm[2][16] = {};
int flagReset[2][16] = {};
void setup()
{
// put your setup code here, to run once:
Serial.begin( 9600 );
}
void loop()
{
// put your main code here, to run repeatedly:
for (int j = 1; j <= 2; j++)
{
for (int i = 1; i <= 2; i++) // Example with 2 modbus address
{
unsigned int address = 40000 + i;
Serial.print ("Modbus address = ");
Serial.println(address, DEC);
pull_data(i);
Serial.print("Test number is ");
Serial.println(test_number);
Findbit(i);
Serial.println("------------------------------------------------- ");
}
}
while (1)
{
}
}
// ---------------Function 1 : Function finding alarm bit-----------------//
void Findbit(int i)
{
for (bit_1 = 0; bit_1 <= 15; bit_1++)
{
Andbit = test_number & 1;
Serial.print("Test number (BINARY) is ");
Serial.println(test_number, BIN);
Serial.print("Check at bit number ");
Serial.println(bit_1);
Serial.print("And bit is ");
Serial.println(Andbit, BIN);
Serial.print("flagAlarm(Before1) = ");
Serial.println(flagAlarm[i][bit_1]);
Serial.print("flagreset(Before1) = ");
Serial.println(flagReset[i][bit_1]);
if (Andbit == 1) //found "1" pass into loop
{
flagAlarm[i][bit_1] = 1;
}
else
{
}
Serial.print("flagAlarm(Before2) = ");
Serial.println(flagAlarm[i][bit_1]);
Serial.print("flagreset(Before2) = ");
Serial.println(flagReset[i][bit_1]);
Set_reset_flag(i,bit_1);
test_number = test_number >> 1;
Serial.print("flagAlarm(After) = ");
Serial.println(flagAlarm[i][bit_1]);
Serial.print("flagreset(After) = ");
Serial.println(flagReset[i][bit_1]);
Serial.println(" ");
}
}
// -----------------------Function 2 : Pull data------------------------- //
int pull_data(int i)
{
i = i - 1;
test_number = test_number_array[i];
return test_number;
}
// -------------Function 3 : Set alarm flag / reset flag ---------------- //
void Set_reset_flag(int i, int bit_1)
{
Serial.print("i = ");
Serial.println(i);
Serial.print("bit_1 = ");
Serial.println(bit_1);
if (flagAlarm[i][bit_1] == 1 && flagReset[i][bit_1] == 0)
{
Serial.print("Alarm at bit ");
Serial.println(bit_1);
flagAlarm[i][bit_1] = 0;
flagReset[i][bit_1] = 1;
}
else if (flagAlarm[i][bit_1] == 0 && flagReset[i][bit_1] == 1)
{
Serial.print("Reset Alarm at bit ");
Serial.println(bit_1);
flagReset[i][bit_1] = 0;
}
else if (flagAlarm[i][bit_1] == 1 && flagReset[i][bit_1] == 1)
{
Serial.print("Alarm still active at bit ");
Serial.println(bit_1);
flagAlarm[i][bit_1] = 0;
flagReset[i][bit_1] = 1;
}
else
{
}
}
Could it be that your bit_1 variable is modified from some other code not mentioned here, or get optimized at all? Also, is it necessary to make a loop counter a global variable? Can you declare it inside the Findbit function?

Arduino - Adafruit 16-channel board, how to properly control all channels with less delay?

I am trying to control a few (8 for now) servo motors using this 16-channel board. I am running to some issues about accuracy, for example, when moving a couple of motors do draw a diagonal line, because of the delay between each servo, each motor will move in different timing resulting in incorrect drawings.
I am not sure about how to drive the motors in the fastest way in therms of code.
Where to set delays, the baud rate settings for this application, etc. I couldn't find a good example using all channels with minimum delay. In my case, messages are coming from serial, as explained in the code comment.
Is this the right way to drive this board channels?
I am using an arduino uno, but I would like to check if using a Teensy 3.2 results in best performances for this application.
Thanks in advance for any suggestions.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
//#define SERVOMIN 150
//#define SERVOMAX 600
// temporary setting pins for 4 lights - it will be controlled by some decade counter...
//#define L1 4
//#define L2 7
//#define L3 8
//#define L4 10
#define L1 9
#define L2 10
#define L3 11
#define L4 12
/*
* a "pointer" device includes a light and 2 servos. Parameters from serial are:
* index,light,servo1,servo2; <- parameters separated by ',' end of pointer is ';'
*
* example of how serial is coming containing instructions for 4 pointers;
0,0,180,180;1,0,0,0;2,0,180,180;3,0,0,0;
0,0,90,90;1,0,90,90;2,0,90,90;3,0,90,90;
**most of the time these instructions doesn't come all for 4 pointers.
ex:
1,0,12,12;4,255,100,100;
**sometimes it comes only id and light parameter.
0,255;1,0;
(instructions only to turn light on/off)
*/
//values for 8 servos:
const uint8_t SERVOMIN[] = {150, 130, 150, 130, 150, 130, 150, 130};
const uint8_t SERVOMAX[] = {600, 500, 600, 500, 600, 500, 600, 500};
//boards (for now, only one board = 16 servos)
Adafruit_PWMServoDriver pwm [] = {
Adafruit_PWMServoDriver(0x40)
};
uint8_t servonum = 0;
uint8_t activeServos = 4; //not being used now
char buf [4]; //maybe too long
uint16_t currentPointer [4]; //index//light//servo1//servo2
byte lightPin [4] = {L1, L2, L3, L4};
uint8_t lightstatus [4] = {0, 0, 0, 0};
//debug
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
boolean feedback = false;
void setup() {
//temporally as digital outputs
pinMode(L1, OUTPUT);
pinMode(L2, OUTPUT);
pinMode(L3, OUTPUT);
pinMode(L4, OUTPUT);
Serial.begin(115200);//230400 //115200 //57600 //38400 ?
for ( uint8_t i = 0; i < sizeof(pwm); i++) {
pwm[i].begin();
pwm[i].setPWMFreq(60);
}
}
void loop() {
reply();
}
void reply() {
if (stringComplete) {
if (feedback) Serial.println(inputString);
// clear the string:
inputString = "";
stringComplete = false;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
}
}
void serialEvent() {
static byte ndx = 0;
static int s = 0;
while (Serial.available()) {
char rc = (char)Serial.read();
inputString += rc;
//(2) setting pointer parameter
if ( rc == ',') {
setPointer(s);
s++;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
ndx = 0;
}
//(3) end of this pointer instruction
else if (rc == ';') {
setPointer(s);
//executePointer(); //plan B
ndx = 0;
s = 0;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
}
//(4) end of command line
else if (rc == '\n') {
//p = 0;
s = 0;
stringComplete = true;
}
//(1) buffering
else {
buf[ndx] = rc;
ndx++;
}
}
}
void setPointer(int s) {
//index//light//servo1//servo2
int value;
value = atoi(buf);
//index
if (s == 0) {
if (feedback) {
Serial.print("index:");
Serial.print(value);
Serial.print(", buf:");
Serial.println(buf);
}
currentPointer[0] = value;
}
//light
else if (s == 1) {
int index = currentPointer[0];
currentPointer[s] = value;
//Serial.println(index);
digitalWrite(lightPin[index], (value > 0) ? HIGH : LOW);
// analogWrite( lightPin[currentPointer[0]], currentPointer[1]); // implement later
if (feedback) {
Serial.print("light: ");
Serial.println(value);
}
//servos
} else {
int index = currentPointer[0];
if (feedback) {
Serial.print("servo ");
Serial.print(index * 2 + s - 2);
Serial.print(": ");
Serial.println(value);
}
uint16_t pulselen = map(value, 0, 180, SERVOMIN[index], SERVOMAX[index]);
currentPointer[s] = pulselen;
pwm[0].setPWM(index * 2 + (s - 2), 0, pulselen); //current pointer id * 2 + s (s is 2 or 3)
//delay(20);
}
}
// this was plan B - not using
void executePointer() {
int index = currentPointer[0];
analogWrite( lightPin[index], currentPointer[1]);
pwm[0].setPWM(index * 2, 0, currentPointer[2]);
pwm[0].setPWM(index * 2 + 1, 0, currentPointer[3]);
delay(20);
}

Arduino mega crashes while reading somewhat large .txt from SD

I have an Arduino mega with an SD Shield and a GSM shield. I'm trying to send one sms to 200 numbers from a text file on the SD card, but the Arduino reboots every time I try to send to over 100 numbers. It doesn't crash when I try to send to 70 numbers. I only read one number at a time so I don't understand the problem. I'm pretty new to Arduino programming.
Please help me this is for a tournament. Here's the code:
#include <avr/pgmspace.h>
#include <SPI.h>
#include <SD.h>
#include <GSM.h>
#define PINNUMBER ""
GSM gsmAccess;
GSM_SMS sms;
// GSM
boolean notConnected;
//char input;
//byte input2;
//char txtContent[200];
byte i = 1;
byte f = 0;
boolean sendit;
//char senderNumber[11];
const String stopp PROGMEM = "Stopp";
//SD
char numbers[11];
//char nmbr;
int l = 0;
//Optimizing
const char q PROGMEM = '\xe5'; // å
const char w PROGMEM = '\xe4'; // ä
const char e PROGMEM = '\xf6'; // ö
const char r PROGMEM = '\xc5'; // Å
const char t PROGMEM = '\xc4'; // Ä
const char y PROGMEM = '\xd6'; // Ö
void setup() {
// txtContent[0] = '\0';
i = 0;
pinMode(53, OUTPUT);
Serial.begin(115200);
// Serial.begin(4800);
Serial.println(F("Connecting to GSM..."));
notConnected = true;
while (notConnected) {
if (gsmAccess.begin(PINNUMBER) == GSM_READY) {
notConnected = false;
}
else {
Serial.println(F("Not Connected"));
delay(1000);
}
}
Serial.println(F("GSM Ready"));
if (!SD.begin(4)) {
Serial.println(F("Error with SD card"));
return;
}
Serial.println(F("SD Ready"));
delay(1000);
Serial.println(F("Write your sms and end it with * and press Send"));
Serial.flush();
}
void loop() {
char txtContent[300];
if (Serial.available() > 0 && !sendit) {
byte input2;
input2 = (int)Serial.read();
txtContent[i] = (char)input2;
if (txtContent[i] == '{') {
txtContent[i] = q;
}
else if (txtContent[i] == '}') {
txtContent[i] = w;
}
else if (txtContent[i] == '|') {
txtContent[i] = e;
}
else if (txtContent[i] == '[') {
txtContent[i] = r;
}
else if (txtContent[i] == ']') {
txtContent[i] = t;
}
else if (txtContent[i] == ';') {
txtContent[i] = y;
}
i++;
if (input2 == '*') {
// Remove the * from text.
for (int j = 0; j < sizeof(txtContent); j++) {
if (txtContent[j] == '*') {
txtContent[j] = '\0';
//Serial.println(txtContent);
}
}
sendit = true;
Serial.flush();
}
else {
sendit = false;
Serial.flush();
}
}
else if (sendit && txtContent[0] != '\0') {
int txtCount = 0;
// else if(sendit){
Serial.println(F("Sending please wait..."));
// Serial.flush();
File myFile;
myFile = SD.open("numbers.txt");
char input;
if (myFile) {
// if (myFile.available()) {
while(myFile.available()){
input = (char)myFile.read();
if (input == ',') {
sms.beginSMS(numbers);
sms.print(txtContent);
// sms.beginSMS("0704941025");
// sms.print("yo");
delay(30);
sms.endSMS();
delay(30);
Serial.println(numbers);
Serial.flush();
// Serial.flush();
for (int j = 0; j < sizeof(numbers); j++) {
if (numbers[i] != '\0') {
numbers[j] = '\0';
}
}
f = 0;
}
else {
numbers[f] = input;
f++;
}
}
myFile.close();
Serial.println(F("All texts have been sent."));
Serial.flush();
} else {
Serial.println(F("error opening numbers.txt"));
}
for (int j = 0; j < sizeof(txtContent); j++) { // Clear text
txtContent[j] = '\0';
}
i = 0;
sendit = false;
}
else {
// delay(1000);
if (sms.available()) {
char senderNumber[11];
sms.remoteNumber(senderNumber, 11);
if (sms.peek() == 'S') {
Serial.print(F("Detta nummer har skickat Stopp: "));
Serial.println(senderNumber);
}
sms.flush();
Serial.flush();
// sendit = false;
}
}
}
Arduino is a strange ground for me but if that is anything like C, then i risk saying sizeof(numbers) will likely return 44, because you're asking for the size of an array in bytes, this probably means your for loop will iterate up to numbers[43] when numbers[] length is 11. Consider this instead:
for (int j = 0; j < sizeof(numbers)/sizeof(numbers[0]); j++) {
Same goes for all the other times you check for the size of an array in this code.
Also, do double check your textfile to ensure its really written in your expected format: 11 numbers then a comma, with no exception. You should maybe try to figure out if f went past 11 digits before hitting a comma and "Fail gracefully" with an error message.

Resources