Binary string isn't converting to decimal properly on my Arduino - c++

I want to convert a binary string I store from my two interrupts (ISR_INT1, ISR_INT0) into a decimal. I've tested this using gcc on my Mac and it works well, giving me the correct decimal. Testing on my Arduino I get a totally different decimal. I know the Arduino only goes up to 16-bit ints so I used a long instead see below. Any help or suggestions are greatly appreciated.
Here is my Arduino sketch:
#define MAX_BITS 100 // max number of bits
#define WEIGAND_WAIT_TIME 3000 // time to wait for another weigand pulse.
unsigned char databits[MAX_BITS]; // stores all of the data bits
unsigned char bitCount; // number of bits currently captured
unsigned char flagDone; // goes low when data is currently being captured
unsigned int weigand_counter; // countdown until we assume there are no more bits
unsigned long facilityCode = 0; // decoded facility code
unsigned long cardCode = 0; // decoded card code
String fullCard; // binary value full card number
int LED_GREEN = 11;
int LED_RED = 12;
int BEEP_BEEP = 10;
// interrupt that happens when INTO goes low (0 bit)
void ISR_INT0() {
fullCard += 0;
bitCount++;
flagDone = 0;
weigand_counter = WEIGAND_WAIT_TIME;
}
// interrupt that happens when INT1 goes low (1 bit)
void ISR_INT1() {
fullCard += 1;
databits[bitCount] = 1;
bitCount++;
flagDone = 0;
weigand_counter = WEIGAND_WAIT_TIME;
}
void setup() {
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(BEEP_BEEP, OUTPUT);
digitalWrite(LED_RED, HIGH); // High = Off
digitalWrite(BEEP_BEEP, HIGH); // High = off
digitalWrite(LED_GREEN, LOW); // Low = On
pinMode(2, INPUT); // DATA0 (INT0)
pinMode(3, INPUT); // DATA1 (INT1)
Serial.begin(9600);
Serial.println("Snow RFID Reader PUT HID CARD ");
// binds the ISR functions to the falling edge of INTO and INT1
attachInterrupt(0, ISR_INT0, FALLING);
attachInterrupt(1, ISR_INT1, FALLING);
weigand_counter = WEIGAND_WAIT_TIME;
}
void loop() {
// This waits to make sure that there have been no more data pulses before processing data
if (!flagDone) {
if (--weigand_counter == 0)
flagDone = 1;
}
// if we have bits and we the weigand counter went out
if (bitCount > 0 && flagDone) {
unsigned char i;
Serial.print(" ");
Serial.print("Read ");
Serial.print(bitCount);
Serial.print(" bits. ");
Serial.print(fullCard);
**// THIS IS WHERE IT I TRY TO CONVERT MY BINARY STRING INTO A DECIMAL**
long fullCardInt = bin2dec(const_cast<char*>(fullCard.c_str()));
Serial.print(fullCardInt);
if (bitCount == 35) {
// 35 bit HID Corporate 1000 format
// facility code = bits 2 to 14
for (i = 2; i < 14; i++) {
facilityCode <<= 1;
facilityCode |= databits[i];
}
// card code = bits 15 to 34
for (i = 14; i < 34; i++) {
cardCode <<= 1;
cardCode |= databits[i];
}
printBits();
} else if (bitCount == 26) {
// standard 26 bit format
// facility code = bits 2 to 9
for (i = 1; i < 9; i++) {
facilityCode <<= 1;
facilityCode |= databits[i];
}
// card code = bits 10 to 23
for (i = 9; i < 25; i++) {
cardCode <<= 1;
cardCode |= databits[i];
}
printBits();
} else {
Serial.println("Unable to decode.");
}
// cleanup and get ready for the next card
bitCount = 0;
facilityCode = 0;
cardCode = 0;
for (i = 0; i < MAX_BITS; i++) {
databits[i] = 0;
}
}
}
void printBits() {
Serial.print("FC = ");
Serial.print(facilityCode);
Serial.print(", CC = ");
Serial.println(cardCode);
digitalWrite(LED_RED, LOW); // Red
if (cardCode == 12345) {
digitalWrite(LED_GREEN, HIGH);
}
delay(500);
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_GREEN, LOW);
digitalWrite(BEEP_BEEP, LOW);
delay(500);
digitalWrite(BEEP_BEEP, HIGH);
delay(500);
digitalWrite(BEEP_BEEP, LOW);
delay(500);
digitalWrite(BEEP_BEEP, HIGH);
}
long bin2dec(const char *bin) {
int result = 0;
for (; *bin; bin++) {
if ((*bin != '0') && (*bin != '1'))
return -1;
result = result * 2 + (*bin - '0');
//if(result<=0) return -1;
}
return result;
}
This is what my serial terminal for my Arduino reads out
Snow RFID Reader PUT HID CARD
// bits read Binary string Incorrect decimal
Read 26 bits. 00001000000100000101010011 16723 FC = 16, CC = 8361
Here is my Test.cpp script
//THIS WORKS AND GIVES ME THE CORRECT DECIMAL
#include <stdio.h>
int bin2dec(const char *bin) {
int result=0;
for(;*bin;bin++) {
if((*bin!='0')&&(*bin!='1'))
return -1;
result=result*2+(*bin-'0');
}
return result;
}
int main (void) {
printf("%d\n", bin2dec("00001000000100000101010011"));
return(0);
}
This is what my test cpp script gives me which is correct.
S:g$ g++ main.cpp -o SnowTest
S:g$ ./SnowTest
2113875

As you said, int on Arduino are 16 bit.
Inside your function int bin2dec, you return an int and result is a int, so in Arduino your number is truncated to the 16 lsb (100000101010011) that in dec are 16723.
Use long instead
long bin2dec(const char *bin)
{
long result=0;
for(;*bin;bin++)
{
if((*bin!='0')&&(*bin!='1'))
return -1;
result=result*2+(*bin-'0');
//if(result<=0) return -1;
}
return result;
}

Related

LED at full brightness after dim button is not touched for certain amount of time

I made a simple 1 button LED dimmer and I would like to have the LED go back to full brightness after the button has not been touched for a set amount of time. But the code I came up with does not seem to be working as I cannot dim the LED
int buttonPin = 12;
int LEDPin = 3;
int buttonVal;
int LEDbright = 0;
int dt = 500;
int fdt = 60000;
void setup() {
// put your setup code here, to run once:
pinMode(buttonPin, INPUT);
pinMode(LEDPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
static unsigned long whenButtonWasTouchedMs = 0;
buttonVal = digitalRead(buttonPin);
Serial.print("Button = ");
Serial.print(buttonVal);
Serial.print(", ");
delay(dt);
if (buttonVal == 1) {
LEDbright = LEDbright + 20;
whenButtonWasTouchedMs = millis();
}
Serial.println(LEDbright);
//
if (LEDbright > 255) {
LEDbright = 255;
delay(dt);
}
unsigned long afterButtonWasTouchedMs = millis() - whenButtonWasTouchedMs;
if (afterButtonWasTouchedMs >= 60000) {
LEDbright = 0;
analogWrite(LEDPin, LEDbright);
}
}
Your question doesn't seem to line up with how the code was written. It looks like you want to do the opposite (have the light get brighter when the button is held down, and then switch off 60 seconds after the button was released).
Here's the code I came up with to help with your stated problem:
int buttonPin = 12;
int LEDPin = 3;
int buttonVal;
int whenButtonWasTouched;
unsigned int LEDbright = 255;
int dt = 500;
int fdt = 60000;
unsigned long whenButtonWasTouchedMs = 0;
void setup() {
// put your setup code here, to run once:
pinMode(buttonPin, INPUT);
pinMode(LEDPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
//static unsigned long whenButtonWasTouchedMs = 0;
buttonVal = digitalRead(buttonPin);
Serial.print("Button = ");
Serial.print(buttonVal);
Serial.print(", ");
delay(dt);
if (buttonVal == 1) {
LEDbright = LEDbright - 20;
whenButtonWasTouchedMs = millis();
}
//
if (LEDbright < 20 || LEDbright > 255) { // The LEDbright > 255 part is because sometimes there can be an integer overflow if we don't keep this.
LEDbright = 0;
}
Serial.println(LEDbright);
unsigned long afterButtonWasTouchedMs = millis() - whenButtonWasTouched;
if (afterButtonWasTouchedMs >= 60000) {
LEDbright = 255;
}
analogWrite(LEDPin, LEDbright);
}
As you can see I changed it so that analogWrite is not in the if statement, it checks for integer overflows, and prints out the adjusted value for the led brightness. To make it grow brighter as the button is held and then switch off after 60 seconds just change the minus sign to a plus sign in the LEDbright adjustment line, and change some of the other values around.
int buttonPin = 12;
int LEDPin = 3;
int buttonVal;
unsigned int LEDbright = 0;
int dt = 500;
int fdt = 60000;
unsigned long whenButtonWasTouchedMs = 0;
void setup() {
// put your setup code here, to run once:
pinMode(buttonPin, INPUT);
pinMode(LEDPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
//static unsigned long whenButtonWasTouchedMs = 0;
buttonVal = digitalRead(buttonPin);
Serial.print("Button = ");
Serial.print(buttonVal);
Serial.print(", ");
delay(dt);
if (buttonVal == 1) {
LEDbright = LEDbright + 20;
whenButtonWasTouchedMs = millis();
}
if (LEDbright > 255) {
LEDbright = 255;
delay(dt);
}
Serial.println(LEDbright);
unsigned long afterButtonWasTouchedMs = millis() - whenButtonWasTouchedMs;
if (afterButtonWasTouchedMs >= 60000) {
LEDbright = 0;
}
analogWrite(LEDPin, LEDbright);
}

converting an int array into a string

I'm currently working on a project that is mostly run on a Raspberry pi, but has tasks complete by microcontroller. Currently, my RPi is sending a string of integers separated by commas to a microcontroller (ESP 32 Thing plus), the microcontroller is then converting that into an intergern array and using those values to set the level of a PWM output. Simultaneously, the microcontroller is reading an analogue input and storing those values into another int array, this data needs to be sent back to the RPi. I've been trying for ages to convert this into a string to be sent back, which works for the most part using sprintf() and strcat(), but it only works when I have just the number values. It would be good to have commas separate the numbers, but as soon as I do that, I get a really weird out put and the numbers start changing from their actual value to "1792" or 44" and it cycles between them or if I us a float array then I get 6 variables correct and then the rest are changes to "512".
Its a lot to send but possibly better to see the whole thing.
#include <stdio.h>
// variables to hold, parse and manipulate data
const byte numChars = 64;
const byte stringLength = 255;
char receivedChars[1000];
char tempChars[1000];
char messageFromPC[stringLength] = {0};
int MyArray[] = {};
int LaserMeasurmentArray[200] = {};
char LaserMeasurmentString[] = "";
int BrakeArray [60] = {10,200,600,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,500,0,0,0};
int AccelleratingArray [60] = {10,200,600,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,500,0,0,0};
int TimeStamps[200] = {};
char TimeStampString = '0';
int i = 0;
int SizeOfArray = 0;
int MyArraySize = 0;
int Counter = 0;
int CounterLaser = 0;
//Clutch/Brake Setup
int ClutchPin = 33;
int BrakePin = 27;
int ButtonPin = 32;
int ButtonState = LOW;
int Delay = 10;
int PWM = 1023;
const int LaserPin = A0;
//Millis variables
unsigned long PreviousMillisAccel = 0;
unsigned long PreviousMillisLaser = 0;
unsigned long CurrentMillis = 0;
const int PeriodWrite = 8;
const int PeriodRead = 5;
//Action States
boolean newData = false;
boolean SendData = false;
boolean AccellerationInProgress = false;
boolean BrakingInProgress = false;
//============
void setup() {
Serial.begin(115200);
//ledcSetup(ledChannel, freq, resolution);
ledcSetup(1, 5000, 10);
ledcSetup(2, 5000, 10);
//ledcAttachPin(LED, ledChannel);
ledcAttachPin(ClutchPin, 1);
ledcAttachPin(BrakePin, 2);
pinMode(LaserPin, INPUT);
pinMode(ButtonPin, INPUT_PULLUP);
}
//============
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
//Serial.print(receivedChars);
//Serial.println();
parseData();
//showParsedData();
//ButtonPress();
ButtonState = HIGH;
while (ButtonState == HIGH) {
ButtonState = digitalRead(ButtonPin);
}//while
Serial.println();
Serial.print("Accellerating");
Serial.println();
PWMSignal();
Serial.println();
Serial.print("Deccellerating");
Serial.println();
PWMSignal2();
newData = false;
ButtonState = HIGH;
while (ButtonState == HIGH) {
ButtonState = digitalRead(ButtonPin);
}//While
SendReply();
}
}
//============
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= stringLength) {
ndx = stringLength - 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
i = 0;
String serialResponse = tempChars;
char buf[sizeof(tempChars)];
serialResponse.toCharArray(buf, sizeof(buf));
char *p = buf;
char *str;
while ((str = strtok_r(p, ",", &p)) != NULL && i < 1180 ){ // delimiter is the comma
MyArray[i] = atoi(str);
//Serial.println(str);
MyArraySize = i;
i++;
}//While
}//parseData
//============
void showParsedData() {
for(int i = 0; i < MyArraySize+1; i++){
Serial.print(MyArray[i]);
Serial.print(",");
}//For
Serial.println();
}
void PWMSignal(){
int Counter = 0;
CounterLaser = 0;
CurrentMillis = millis();
AccellerationInProgress = true;
while(AccellerationInProgress == true) {
CurrentMillis = millis();
if (Counter == 60){
AccellerationInProgress = false;
}//if
else if (CurrentMillis - PreviousMillisAccel >= PeriodWrite) {
ledcWrite(1, AccelleratingArray[Counter]);
//Serial.println(AccelleratingArray[Counter]);
Counter = Counter + 1;
PreviousMillisAccel = CurrentMillis;
}//else if
if (CurrentMillis - PreviousMillisLaser >= PeriodRead){
LaserMeasurmentArray[CounterLaser] = analogRead(LaserPin);
Serial.println(LaserMeasurmentArray[CounterLaser]);
TimeStamps [CounterLaser] = CurrentMillis;
PreviousMillisLaser = CurrentMillis;
CounterLaser = CounterLaser + 1;
}//if
}//While Accel
ledcWrite(1, 0);
}//PWMSignal
void PWMSignal2() {
int Counter = 0;
BrakingInProgress = true;
while(BrakingInProgress == true) {
CurrentMillis = millis();
if (Counter == 60){
BrakingInProgress = false;
}//if
else if (CurrentMillis - PreviousMillisAccel >= PeriodWrite) {
ledcWrite(2, BrakeArray[Counter]);
//Serial.println(BrakeArray[Counter]);
Counter = Counter + 1;
PreviousMillisAccel = CurrentMillis;
}//else if
if (CurrentMillis - PreviousMillisLaser >= PeriodRead){
LaserMeasurmentArray[CounterLaser] = analogRead(LaserPin);
Serial.println(LaserMeasurmentArray[CounterLaser]);
TimeStamps [CounterLaser] = CurrentMillis;
PreviousMillisLaser = CurrentMillis;
CounterLaser = CounterLaser + 1;
}//if
}//While Accel
ledcWrite(1, 0);
ledcWrite(2, 0);
}//PWMSignal2
void SendReply() {
//Serial.println("Laser Data");
//Serial.println(sizeof(LaserMeasurmentArray));
for (int i = 0; i<=200; i++){
Serial.println(LaserMeasurmentArray[i]);
}
for (int i = 0; i <= 200; i++){
int buff = LaserMeasurmentArray[i];
char str [7];
sprintf(str,"%u" ,buff);
strcat(LaserMeasurmentString,str);
strcat(LaserMeasurmentString, ",");
}
Serial.print(LaserMeasurmentString);
}//sendReply
If someone could point out what I am doing wrong or have misunderstood, that would be great, thank you.

Arduino/C++ adjusting Long Millis () in loop

So I have made a simple Arduino circuit for practice. I am able to start and stop the loop at any moment via a button. Now I want to make some sick led light sequences.
My initial thought is changing the delay_led1 function at the end of the loop. That way I am able to change the sequence when the loop ends.
I tried to do this with: interval_led1 + 300 (marked in my code with alot of /////////) at the end of the loop, but this doens't work.
How could I add milliseconds to the interval_led1 function via the loop?
const int led1 = 13;
const int led2 = 8;
const int led3 = 5;
const int button = 10;
int ledState_led1 = LOW; // ledState used to set the LED
int ledState_led2 = LOW;
int ledState_led3 = LOW;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis_led1 = 0; // will store last time LED was updated
unsigned long previousMillis_led2 = 0;
unsigned long previousMillis_led3 = 0;
long interval_led1 = 500; // interval at which to blink (milliseconds)
long interval_led2 = 600;
long interval_led3 = 700;
boolean buttonstate = false;
void setup() {
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(button, INPUT);
}
void loop() {
// check to see if it's time to blink the LED; that is, if the difference
// between the current time and last time you blinked the LED is bigger than
// the interval at which you want to blink the LED.
unsigned long currentMillis_led1 = millis();
unsigned long currentMillis_led2 = millis();
unsigned long currentMillis_led3 = millis();
bool current_state = digitalRead(button);
bool prev_buttonstate= false;
if(current_state==HIGH && current_state != prev_buttonstate) {
buttonstate = !buttonstate; //reverse buttonstate value
}
prev_buttonstate = current_state;
if(buttonstate==true)
if (currentMillis_led1 - previousMillis_led1 >= interval_led1) {
previousMillis_led1 = currentMillis_led1;
if (ledState_led1 == LOW) {
ledState_led1 = HIGH;
} else {
ledState_led1 = LOW;
}
digitalWrite(led1, ledState_led1);
////////interval_led1 + int(3000); ///////////////////
}
if(buttonstate==true)
if (currentMillis_led2 - previousMillis_led2 >= interval_led2) {
previousMillis_led2 = currentMillis_led2;
if (ledState_led2 == LOW) {
ledState_led2 = HIGH;
} else {
ledState_led2 = LOW;
}
digitalWrite(led2, ledState_led2);
}
if(buttonstate==true)
if (currentMillis_led3 - previousMillis_led3 >= interval_led3) {
previousMillis_led3 = currentMillis_led3;
if (ledState_led3 == LOW) {
ledState_led3 = HIGH;
} else {
ledState_led3 = LOW;
}
digitalWrite(led3, ledState_led3);
}
}
Did you mean to have:
Interval_led = interval_led1 + 3000;
Where you actually assign a new value to that variable?

function that set pin high for defined number of times and time

I have this problem where I try to set an output pin high for a set time and times.
I do the call with hapticFeedback(1000, 2, 1);
the variables are defined as
unsigned long hapticPreviousMillis = 0;
int hapticState = LOW;
int oneshotHaptic = 0;
here is the function. For some reason I only get the pin set HIGH and not the blinks and LOW
void hapticFeedback(int activeLength, int repeats, int oneshotHaptic) {
if (oneshotHaptic == 1) {
for (int x = 0; x <= repeats; x++) {
unsigned long currentMillis = millis();
if (currentMillis - hapticPreviousMillis >= (unsigned long)activeLength) {
hapticPreviousMillis = currentMillis;
if (hapticState == LOW) {
hapticState = HIGH;
}
else {
hapticState = LOW;
}
digitalWrite(haptic, hapticState);
}
}
}
oneshotHaptic = 0;
}
So I figured it out and if anyone else is looking for this here is what I came up with. It might not be the smoothest of code but it does what I intended it to do
in the loop I have
if (setOneshotHaptic == 1) {
hapticFeedback(activeLength);
}
and the haptic function look like this
void hapticFeedback(int activeLength) {
unsigned long currentMillis = millis();
if (currentMillis - hapticPreviousMillis >= (unsigned long)activeLength) {
hapticPreviousMillis = currentMillis;
if (x == repeats) {
setOneshotHaptic = false;
hapticState = HIGH;
x = 0;
}
if (hapticState == LOW) {
hapticState = HIGH;
x++;
}
else {
hapticState = LOW;
}
digitalWrite(haptic, hapticState);
}
}
whenever i like to have haptic feedback i can define the following vars
setOneshotHaptic = true;
repeats = 3;
activeLength = 1000;
When the number of repeats has been reached I lay down the oneshot, force the output to high for it to be low by the routine and finally reset my repeat counter.
There might be nicer ways to do this. However I couldn't find them and this works for me....

if statement doesn't work on Nucleo64 but it does on Maple mini

I have a code that works on Maple Mini but I have to change hardware to Nucleo F030r8 because it has more ADC's and it totally sucks.
In the modbus_update() function there is a check of the size of inputBuffer and the following code should run only if the value of this variable is bigger than 0.
if (inputBuffer > 0 && micros() - microsFlag >= T3_5) {
...
}
But it runs even if value of inputBuffer is exactly 0. The strange thing is that this code (with different serial ports opening method) runs perfectly on Maple Mini. Does anyone have any idea what can be be the problem?
Here is the whole code:
#define BAUDRATE 19200
#define RE_PIN PC12
#define TE_PIN PF4
#define LED_PIN LED_BUILTIN
#define SLAVE_ID 1
#define BUFFER_SIZE 256 // max frame size
#define READ_INTERNAL_REGISTERS 4 // function code
#define MIN_REGISTER_ADDRESS 30001
#define MAX_REGISTER_ADDRESS 30020
#define MAX_SENSORS_PER_ROW 10
#define SENSORS_PER_ROW 7
#define MAX_ROWS 2
#define ROWS 1
#define DEBUG true
#define INVALID_VALUE 0x7FFF
const byte INPUTS[] = {PA0, PA1, PA4, PB0, PC1, PC0};
unsigned char frame[BUFFER_SIZE];
unsigned char functionCode;
unsigned int T1;
unsigned int T1_5; // inter character time out
unsigned int T3_5; // frame delay
unsigned long millisFlag = 0;
unsigned long microsFlag = 0;
unsigned char inputBuffer = 0;
int dlyCounter = 0;
int16_t sensorVals[MAX_SENSORS_PER_ROW * MAX_ROWS];
/*
HardwareSerial *modbus = &Serial;
HardwareSerial Serial1(PA10, PA9);
HardwareSerial *debug = &Serial1;
*/
HardwareSerial debug(PA10, PA9);
void setup() {
pinMode(LED_PIN, OUTPUT);
for (int i = 0; i < SENSORS_PER_ROW; i++) {
pinMode(INPUTS[i], INPUT_PULLUP);
}
debug.begin(BAUDRATE, SERIAL_8E1);
modbus_configure(BAUDRATE, 0); // baud rate, low latency
microsFlag = micros();
}
void loop() {
readSensorVals(100);
modbus_update();
}
unsigned int modbus_update() {
unsigned char overflow = 0;
while (Serial.available()) {
if (overflow) {
Serial.read(); // empty the input buffer
} else {
if (inputBuffer == BUFFER_SIZE) {
overflow = 1;
} else {
frame[inputBuffer] = Serial.read();
inputBuffer++;
}
}
microsFlag = micros();
}
// If an overflow occurred return to the main sketch
// without responding to the request i.e. force a timeout
if (overflow) {
debug.println("Error: input buffer overflow");
return 0;
}
// if inter frame delay occurred, check the incoming package
if (inputBuffer > 0 && micros() - microsFlag >= T3_5) {
debug.println("\nIncoming frame:");
for (int i = 0; i < inputBuffer; i++) {
debug.print(frame[i], HEX);
debug.print(" ");
}
debug.println();
// check CRC
unsigned int crc = ((frame[inputBuffer - 2] << 8) | frame[inputBuffer - 1]); // combine the crc Low & High bytes
if (calculateCRC(frame, inputBuffer - 2) != crc) {
debug.println("Error: checksum failed");
inputBuffer = 0;
return 0;
}
debug.println("CRC OK");
// check ID
unsigned char id = frame[0];
if (id > 242) {
debug.println("Error: Invalid ID");
inputBuffer = 0;
return 0;
}
// check if it's a broadcast message
if (id == 0) {
debug.println("Broadcast message");
inputBuffer = 0;
return 0;
}
if (id != SLAVE_ID) {
debug.println("Not my ID");
inputBuffer = 0;
return 0;
}
debug.println("ID OK");
// check function code
functionCode = frame[1];
if (functionCode != READ_INTERNAL_REGISTERS) {
debug.println("Exception: illegal function");
exceptionResponse(1);
inputBuffer = 0;
return 0;
}
debug.println("Function code OK");
// check frame size (function 4 frame MUST be 8 bytes long)
if (inputBuffer != 8) {
// some workaround here:
//if (inputBuffer != 8 || !(inputBuffer == 9 && frame[inputBuffer] == 0)) {
debug.println("Error: inaccurate frame length");
inputBuffer = 0;
return 0;
}
debug.println("Frame size OK");
// check data address range
unsigned int noOfRegisters = ((frame[4] << 8) | frame[5]); // combine the number of register bytes
if (noOfRegisters > 125) {
debug.println("Exception: illegal data address");
exceptionResponse(2);
inputBuffer = 0;
return 0;
}
unsigned int firstRegAddress = ((frame[2] << 8) | frame[3]); // combine the starting address bytes
debug.print("First address: ");
debug.println(firstRegAddress);
unsigned int lastRegAddress = firstRegAddress + noOfRegisters - 1;
debug.print("Last address: ");
debug.println(lastRegAddress);
unsigned char noOfBytes = noOfRegisters * 2;
unsigned char responseFrameSize = 5 + noOfBytes; // ID, functionCode, noOfBytes, (dataLo + dataHi) * number of registers, crcLo, crcHi
unsigned char responseFrame[responseFrameSize];
responseFrame[0] = SLAVE_ID;
responseFrame[1] = 0x04;
responseFrame[2] = noOfBytes;
unsigned char address = 3; // PDU starts at the 4th byte
for (int index = (int)(firstRegAddress - MIN_REGISTER_ADDRESS); index <= (int)(lastRegAddress - MIN_REGISTER_ADDRESS); index++) {
int16_t temp = (index >= 0 && index < MAX_ROWS * MAX_SENSORS_PER_ROW) ? sensorVals[index] : INVALID_VALUE;
responseFrame[address] = temp >> 8; // split the register into 2 bytes
address++;
responseFrame[address] = temp & 0xFF;
address++;
}
unsigned int crc16 = calculateCRC(responseFrame, responseFrameSize - 2);
responseFrame[responseFrameSize - 2] = crc16 >> 8; // split crc into 2 bytes
responseFrame[responseFrameSize - 1] = crc16 & 0xFF;
debug.println("Frame to send:");
for (int i = 0; i < responseFrameSize; i++) {
debug.print(responseFrame[i], HEX);
debug.print(" ");
}
debug.println();
sendPacket(responseFrame, responseFrameSize);
inputBuffer = 0;
while (Serial.available()) { // empty input buffer
Serial.read();
}
}
}
void modbus_configure(long baud, unsigned char _lowLatency) {
Serial.begin(baud, SERIAL_8E1);
pinMode(TE_PIN, OUTPUT);
pinMode(RE_PIN, OUTPUT);
rxEnable(); // pin 0 & pin 1 are reserved for RX/TX. To disable set TE and RE pin < 2
if (baud == 1000000 && _lowLatency) {
T1 = 1;
T1_5 = 1;
T3_5 = 10;
} else if (baud >= 115200 && _lowLatency) {
T1 = 50;
T1_5 = 75;
T3_5 = 175;
} else if (baud > 19200) {
T1 = 500;
T1_5 = 750;
T3_5 = 1750;
} else {
T1 = 10000000 / baud;
T1_5 = 15000000 / baud; // 1T * 1.5 = T1.5
T3_5 = 35000000 / baud; // 1T * 3.5 = T3.5
}
}
void exceptionResponse(unsigned char exception) {
unsigned char responseFrameSize = 5;
unsigned char responseFrame[responseFrameSize];
responseFrame[0] = SLAVE_ID;
responseFrame[1] = (functionCode | 0x80); // set the MSB bit high, informs the master of an exception
responseFrame[2] = exception;
unsigned int crc16 = calculateCRC(responseFrame, 3); // ID, functionCode + 0x80, exception code == 3 bytes
responseFrame[3] = crc16 >> 8;
responseFrame[4] = crc16 & 0xFF;
sendPacket(responseFrame, responseFrameSize); // exception response is always 5 bytes (ID, functionCode + 0x80, exception code, 2 bytes crc)
}
unsigned int calculateCRC(unsigned char f[], byte bufferSize) {
unsigned int temp, temp2, flag;
temp = 0xFFFF;
for (unsigned char i = 0; i < bufferSize; i++) {
temp = temp ^ f[i];
for (unsigned char j = 1; j <= 8; j++) {
flag = temp & 0x0001;
temp >>= 1;
if (flag)
temp ^= 0xA001;
}
}
// Reverse byte order.
temp2 = temp >> 8;
temp = (temp << 8) | temp2;
temp &= 0xFFFF;
return temp; // the returned value is already swapped - crcLo byte is first & crcHi byte is last
}
void rxEnable() {
if (TE_PIN > 1 && RE_PIN > 1) {
digitalWrite(TE_PIN, LOW);
digitalWrite(RE_PIN, LOW);
digitalWrite(LED_PIN, LOW);
}
}
void txEnable() {
if (TE_PIN > 1 && RE_PIN > 1) {
digitalWrite(TE_PIN, HIGH);
digitalWrite(RE_PIN, HIGH);
digitalWrite(LED_PIN, HIGH);
}
}
void sendPacket(unsigned char f[], unsigned char bufferSize) {
txEnable();
delayMicroseconds(T3_5);
for (unsigned char i = 0; i < bufferSize; i++) {
Serial.write(f[i]);
}
Serial.flush();
delayMicroseconds(T3_5); // allow frame delay to indicate end of transmission
rxEnable();
}
// #param dly delay between sensor readings in milliseconds
void readSensorVals(int dly) {
if (millis() != millisFlag) {
dlyCounter++;
millisFlag = millis();
}
if (dlyCounter >= dly) { // read sensor values
for (int i = 0; i < MAX_ROWS; i++) {
for (int j = 0; j < MAX_SENSORS_PER_ROW; j++) {
int actualIndex = i * MAX_SENSORS_PER_ROW + j;
if (i < ROWS && j < SENSORS_PER_ROW) {
sensorVals[actualIndex] = random(20, 30);
//sensorVals[actualIndex] = (4096 - analogRead(INPUTS[j])) / 20 - 133;
} else {
sensorVals[actualIndex] = INVALID_VALUE;
}
}
}
dlyCounter = 0;
}
}