I'm working on a project with arduino UNO, i'm using freertos for multitasking, i have two tasks :
1-TaskLedBlink.
and 2-TaskSerialP6008.
the first task is used to flash a led twice every given period of time and
the second one is used to receive a frame from serial port and then send it back. the frame should start with 0x02 and ends with 0x0D. when the 0x02 is received, i start adding incoming bytes to an array called asciiFrame until 0x0D was received, after that i send the hole frame back. Until that moment the program was working just fine. then i decided to add a block of code wich is responsible for decoding the asciiFrame, the result is saved in an array called binaryFrame. Here the program's started to act strange . the led stops flashing, sometimes is on sometimes is off, serial data are lost (not received). in the worst cases the program is no more responding until i reboot the arduino.
When i started looking around to solve the problem i found that when i remove this line binaryFrame[m]=(asciiFrame[k]&15)+((asciiFrame[k+1]&15)<<4); the program works fine. Help please solve this problem...here is the code
#include <Arduino_FreeRTOS.h>
#include <semphr.h> // add the FreeRTOS functions for Semaphores (or Flags).
byte inByte = 0; // incoming serial byte
bool startByte = false;
byte asciiFrame[18]; //array to save ascci frame p6008 02 xx xx....0d max 18 bytes including 0x02 and 0x0D
byte binaryFrame[8]; //array to save binary frame p6008 (max 7 bytes + CHECKSUM)
byte i=0; //index used to select case in array
byte j=0; //index used to select byte in array to send it over serial
byte k=0; //index used to convert from ascii to binary
byte m=0; //to save data in binary frame
bool asciiFrameComplete = false; //to indicate that a complete frame(ascii mode) hs been recieved
bool binaryFrameComplete = false;
bool asciiP6008Enabled = true; //true(ascii mode) false(binary mode)
// Declare a mutex Semaphore Handle which we will use to manage the Serial Port.
// It will be used to ensure only only one Task is accessing this resource at any time.
SemaphoreHandle_t xSerialSemaphore;
// define two Tasks for DigitalRead & AnalogRead
void TaskLedBlink( void *pvParameters );
void TaskSerialP6008( void *pvParameters );
// the setup function runs once when you press reset or power the board
void setup() {
// initialize serial communication at 9600 bits per second:
pinMode(13, OUTPUT);
Serial.begin(9600);
// Semaphores are useful to stop a Task proceeding, where it should be paused to wait,
// because it is sharing a resource, such as the Serial port.
// Semaphores should only be used whilst the scheduler is running, but we can set it up here.
if ( xSerialSemaphore == NULL ) // Check to confirm that the Serial Semaphore has not already been created.
{
xSerialSemaphore = xSemaphoreCreateMutex(); // Create a mutex semaphore we will use to manage the Serial Port
if ( ( xSerialSemaphore ) != NULL )
xSemaphoreGive( ( xSerialSemaphore ) ); // Make the Serial Port available for use, by "Giving" the Semaphore.
}
// Now set up two Tasks to run independently.
xTaskCreate(
TaskLedBlink
, (const portCHAR *)"LedBlink" // A name just for humans
, 128 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 2 // Priority, with 1 being the highest, and 4 being the lowest.
, NULL );
xTaskCreate(
TaskSerialP6008
, (const portCHAR *) "AnalogRead"
, 256 // Stack size
, NULL
, 1 // Priority
, NULL );
// Now the Task scheduler, which takes over control of scheduling individual Tasks, is automatically started.
//vTaskStartScheduler();
}
void loop(){
// Empty. Things are done in Tasks.
}
/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/
void TaskLedBlink( void *pvParameters __attribute__((unused)) ) // This is a Task.
{
for (;;) // A Task shall never return or exit.
{
digitalWrite(13, HIGH);
vTaskDelay(1);
digitalWrite(13, LOW);
vTaskDelay(6);
digitalWrite(13, HIGH);
vTaskDelay(1);
digitalWrite(13, LOW);
vTaskDelay(17);
}
}
void TaskSerialP6008( void *pvParameters __attribute__((unused)) ) // This is a Task.
{
for (;;)
{
// read the input on analog pin 0:
//int sensorValue = analogRead(A0);
if (Serial.available()>0)
{
inByte = Serial.read();
if ((inByte == 2 || startByte)&&asciiP6008Enabled) //if 2 was received or already has been(startByte), and ascii mode is enabled
{
startByte = true; //start byte came
asciiFrame[i] = inByte; //save bytes in frame array
i++; //increment to the next case in frame array
if (inByte == 13) //end byte came
{
asciiFrameComplete = true;
startByte = false;
i=0;
if ( xSemaphoreTake( xSerialSemaphore, ( TickType_t ) 5 ) == pdTRUE )
{
for (j=0;j<18&&asciiFrame[j]!=0;j++) //send frame back
Serial.write(asciiFrame[j]);
memset(asciiFrame, 0, sizeof(asciiFrame)); //then clear it
}
xSemaphoreGive( xSerialSemaphore ); // Now free or "Give" the Serial Port for others.
for(k=1 ; k<sizeof(asciiFrame) ; k++)
{
if(asciiFrame[k]==13)
{
binaryFrameComplete=true;
m=0;
break;
}
if(k%2!=0)
{
binaryFrame[m]=(asciiFrame[k]&15)+((asciiFrame[k+1]&15)<<4);
m++;
}
}
// if ( xSemaphoreTake( xSerialSemaphore, ( TickType_t ) 5 ) == pdTRUE )
// {
// for (j=0;j<8;j++) //send frame back
// Serial.write(binaryFrame[j]);
// memset(asciiFrame, 0, sizeof(asciiFrame)); //then clear it
// memset(binaryFrame, 0, sizeof(asciiFrame)); //then clear it
// }
// xSemaphoreGive( xSerialSemaphore ); // Now free or "Give" the Serial Port for others.
}
if (i==sizeof(asciiFrame)) //if array is full ecrase received data, frame length should not exceed 18 bytes
{
memset(asciiFrame, 0, sizeof(asciiFrame)); //clear array
i=0; //init i
startByte = false;
}
}
else if(!asciiP6008Enabled) //binary enabled
{
Serial.println("binary enabled");
}
}
//vTaskDelay(1); // one tick delay (15ms) in between reads for stability
}
}
I think the problem was declaring the variables as global, i've declared them inside the task and it's working pretty good.
seems you're near the RAM limits...
or your m exceeds the binaryFrame limits...
By quickly looking, I estimate m being about half of k, and you defined
byte asciiFrame[18];
byte binaryFrame[8];
and if there's no 0x0D, you won't reset m ...
With local variables you get a different behavior when spoofing memory.
Related
Here is the code from my test environment. it simply replies back (echo) the entered data to the terminal application where it is connected through a Serial Port. So far so good.
Now, I need to use that data and process as per the requirements/protocol.
But, I am struggling to consume the data UART0Buffer as I expected.
(The variable UART0Buffer is populated in the UART Interrupt)
I send e.g. "7E0007AA010203040506CC" from the terminal
and the code replies back this to the terminal application in an order in four steps for some reason;
1 by Process #1
37
2 by Process #2
00
3 by Process #1
30 30 30 37 41 41 30 31 30 32 30 33 30 34 30
4 by Process #2
A1
As you can also notice that the data replied (echoed) is not identical with what I send from the terminal.
And furthermore, if I remove line "Process #2" completely,
I can get a proper echo;
37 45 30 30 30 37 41 41 30 31 30 32 30 33 30 34 30 35 30 36 43 43
What am I really missing here?
int main()
{
SystemInit(); /* Clock and PLL configuration */
UARTInit(0, 57600); /* baud rate setting */
while(1)
{
if ( UART0Count != 0 )
{
LPC_UART0->IER = IER_THRE | IER_RLS; /* Disable RBR */
UARTSend( 0, (uint8_t *)UART0Buffer, UART0Count ); // Process #1
UART0Count = 0;
// I need to call a function here in order to process data in UART0Buffer
// like checking/using STX, CHKSUM and DATA.
// A test, if I can manage to use the data in pieces
UARTSend( 0, (uint8_t *)UART0Buffer[0], 1); // Process #2
LPC_UART0->IER = IER_THRE | IER_RLS | IER_RBR; /* Re-enable RBR */
}
}
}
void UARTSend( uint32_t portNum, uint8_t *BufferPtr, uint32_t Length )
{
if ( portNum == 0 )
{
while ( Length != 0 )
{
/* THRE status, contain valid data */
while ( !(UART0TxEmpty & 0x01) );
LPC_UART0->THR = *BufferPtr;
UART0TxEmpty = 0; /* not empty in the THR until it shifts out */
BufferPtr++;
Length--;
}
}
}
EDIT:
As dude has already explained the wrong use of "Process #2", please ignore it. It somehow breaks things. However, my issue persists. Although I can get an echo by "Process #1", I couldn't manage to read and use the data!?! Any pointers would be greatly appreciated.
EDIT:
Receiving:
void UART0_IRQHandler (void)
{
uint8_t IIRValue, LSRValue;
uint8_t Dummy = Dummy;
IIRValue = LPC_UART0->IIR;
IIRValue >>= 1; /* skip pending bit in IIR */
IIRValue &= 0x07; /* check bit 1~3, interrupt identification */
if ( IIRValue == IIR_RLS ) /* Receive Line Status */
{
LSRValue = LPC_UART0->LSR;
/* Receive Line Status */
if ( LSRValue & (LSR_OE|LSR_PE|LSR_FE|LSR_RXFE|LSR_BI) )
{
/* There are errors or break interrupt */
/* Read LSR will clear the interrupt */
UART0Status = LSRValue;
Dummy = LPC_UART0->RBR; /* Dummy read on RX to clear
interrupt, then bail out */
return;
}
if ( LSRValue & LSR_RDR ) /* Receive Data Ready */
{
/* If no error on RLS, normal ready, save into the data buffer. */
/* Note: read RBR will clear the interrupt */
UART0Buffer[UART0Count] = LPC_UART0->RBR;
UART0Count++;
if ( UART0Count == BUFSIZE )
{
UART0Count = 0; /* buffer overflow */
}
}
}
else if ( IIRValue == IIR_RDA ) /* Receive Data Available */
{
/* Receive Data Available */
UART0Buffer[UART0Count] = LPC_UART0->RBR;
UART0Count++;
if ( UART0Count == BUFSIZE )
{
UART0Count = 0; /* buffer overflow */
}
}
else if ( IIRValue == IIR_CTI ) /* Character timeout indicator */
{
/* Character Time-out indicator */
UART0Status |= 0x100; /* Bit 9 as the CTI error */
}
else if ( IIRValue == IIR_THRE ) /* THRE, transmit holding register empty */
{
/* THRE interrupt */
LSRValue = LPC_UART0->LSR; /* Check status in the LSR to see if
valid data in U0THR or not */
if ( LSRValue & LSR_THRE )
{
UART0TxEmpty = 1;
}
else
{
UART0TxEmpty = 0;
}
}
}
You're basically designing a serial message driver. Divide the design into layers.
The lowest layer is the UART driver that moves characters to and from the hardware UART. The UART driver encapsulates all the knowledge about how to interface to the UART hardware. This layer knows nothing of frames or messages, only characters. It copies a transmit character to the TX register when a transmit character is available and the UART is ready to transmit. It copies a received character from the UART RX register when one is available. These actions are typically implemented in the UART interrupt service routine. And typically, RAM circular buffers are used to store the TX and RX characters.
The next layer is for framing characters. This layer is responsible for identifying the characters that delimit a frame. This layer encapsulates all the knowledge about characters are delimited into frames. This is typically implemented as a state machine that gets repeatedly called when characters are available in the RX buffer. The state machine gets the next character from the RX buffer and processes it according to the current state. States might include, for example, waiting_for_start_of_frame, copying_body_of_frame, and waiting_for_end_of_frame. If the frame protocol includes a frame check sequence then this layer performs the check. This layer doesn't attempt to interpret the meaning of frames, rather it just forms complete frames to be used by the next layer.
If your serial messages are large enough to span multiple frames then you might have a layer here that stitches frames together into messages. Or if each message is a contained within a single frame then you probably don't need this layer.
At the top is the layer that actually interprets the message and performs the appropriate actions. This layer doesn't know anything about the UART or the frame delimiters because all of that knowledge is encapsulated by the lower layers.
This answer has more tips, Embedded C UART conventions
I am observing some odd behavior in my PIC code where I copy data from a
global variable that is written by an ISR into two different buffers
consecutively, but the value only seems to successfully get written into
one of the two buffers. When I try this with a different variable that is
not written by an ISR, it correctly gets copied into both buffers.
My setup is a PIC16LF15355 using the XC8 compiler version 1.45. I am
configuring one of the PIC MSSP modules as a SPI interface to write
measurement data and switch states to an RF transceiver. I have a another
remote RF transceiver that receives and processes the data with a
PIC16LF15355 via a SPI interface. For both PICs, I am configuring the
second MSSP module as an I2C interface connected to a Raspberry Pi for
debugging purposes. Any data that is written to the SPI buffer on the
transmitting end is also copied to the I2C buffer so that it can be
periodically read to see what is being sent. Likewise, the data read
into the SPI buffer on the receiving end is copied to an I2C buffer so
that it can be read periodically to see what is being received.
There is a switch input on the transmitting end that is debounced with
a timer ISR (timer0_handler in the code below). The state gets written
into sw1State by this ISR. When it comes time to transmit data, the
switch state and other measurement data gets copied into the I2C buffer
and then the I2C buffer gets copied into the SPI buffer for transmission.
When I sample the I2C buffer contents periodically from the Pi, I can see
switch state changing in response to presses of the switch. But when I
sample the I2C buffer contents periodically on the receiving end, I only
ever see the switch state default value (1).
What is strange is that if I change the code to copy the value from a
variable that is not written by an ISR, the value shows up correctly on
the receiving end. But copy from the I2C buffer to the SPI buffer is
just a generic copy of one array to another, and all other measurement
data shows up correctly on the receiving end. The two buffers should
be identical. And there is nothing in my code that modifies the SPI
buffer between the time when it gets copied from the I2C buffer and
when the SPI buffer gets written out to the transceiver.
A stripped down copy of the code is included below. I've been looking
at this for several days and I just don't see what is causing this.
volatile uint8_t sw1State; // current switch 1 state
volatile uint8_t sw1TimeExpired; // true if switch 1 debounce time expired
uint8_t spiOutBuf[13]; // SPI output buffer
uint8_t spiInBuf[13]; // SPI input buffer
uint8_t i2cBuff[13]; // I2C input/output buffer
void interrupt main_ISR( void ) {
I2C_slave_handler();
timer0_handler();
switch_input_handler();
}
int main( void ) {
... device initialization code omitted for brevity
sw1State = 1;
sw1TimeExpired = 1;
for ( ; ; )
{
// Read the ADC values and add to the I2C buffer (for debugging).
for ( uint8_t chan = 0; chan < 9; chan++ )
{
i2cBuff[chan + 1] = ADC_read( chan );
}
// Read the switch status and add to the I2C buffer.
i2cBuff[10] = sw1State;
// Load the write transmit payload command into the SPI buffer.
spiOutBuf[0] = CMD_W_TX_PAYLOAD;
// Copy the payload data from the I2C buffer to the SPI buffer.
for ( uint8_t i = 1; i < 13; i++ )
{
spiOutBuf[i] = i2cBuff[i];
}
// Write the SPI buffer to the transceiver module payload register.
writeReadSPI( 13 );
// Clear the RB4 interrupt-on-change interrupt flag (IRQ change) and
// set CE high to initiate the transmit. Hold CE high until an ack
// is received or there is an ack timeout.
IOCBFbits.IOCBF4 = 0;
CE = 1;
__delay_us( 130 ); // ensure minimum state change transition time
... code here omitted which checks the transceiver status
__delay_ms( 80 );
}
}
void switch_input_handler( void ) {
// Check for switch 1 trigger.
if ( IOCCFbits.IOCCF6 == 1 )
{
IOCCFbits.IOCCF6 = 0; // clear IOC interrupt
// Process switch 1 trigger if debounce time has expired.
if ( sw1TimeExpired == 1 )
{
// Toggle between OFF state and ON state.
if ( sw1State == 1 )
{
sw1State = 2;
}
else
{
sw1State = 1;
}
// Load and restart Timer0 with 2-second counter value.
T0CON0bits.T0EN = 0;
TMR0H = 0xc2;
TMR0L = 0xf7;
T0CON0bits.T0EN = 1;
sw1TimeExpired = 0;
}
}
}
void timer0_handler( void ) {
if ( PIR0bits.TMR0IF == 1 )
{
PIR0bits.TMR0IF = 0; // clear the interrupt
T0CON0bits.T0EN = 0; // disable Timer0
if ( sw1TimeExpired == 0 )
{
sw1TimeExpired = 1; // Note the switch 1 timeout for switch handler.
// No state transition here. This just enables
// the next switch interrupt to change state.
}
}
}
void I2C_slave_handler( void ) {
... code omitted for brevity
}
From the code snippet it appears that the SPI channel is transmitting as a background task, while the I2C channel is transmitting in the foreground (interrupt handler). This could potentially result in a race condition and explain the behavior you are experiencing.
Recommend you try using a single transmit buffer for both channels, and set a variable to indicate when both channels have successfully sent the the data. Once both channels indicate data transmitted, then generate a fresh data packet/buffer for the next cycle.
I have recently got my hands on a Tiva C series MCU and I would like to use it's USB capabilities.
My goal is to send commadns to the boards via USB and get a message back (acknowledge or error reporting)
Commands start with a capital letter followed by 3 digits like X123.
There is a sample code which I have modified to a bit, to get certain responses if the RxBuffer has the letter in it.
static uint32_t
EchoNewDataToHost(tUSBDBulkDevice *psDevice, uint8_t *pui8Data,
uint32_t ui32NumBytes)
{
uint32_t ui32Loop, ui32Space, ui32Count;
uint32_t ui32ReadIndex;
uint32_t ui32WriteIndex;
tUSBRingBufObject sTxRing;
//
// Get the current buffer information to allow us to write directly to
// the transmit buffer (we already have enough information from the
// parameters to access the receive buffer directly).
//
USBBufferInfoGet(&g_sTxBuffer, &sTxRing);
//
// How much space is there in the transmit buffer?
//
ui32Space = USBBufferSpaceAvailable(&g_sTxBuffer);
//
// How many characters can we process this time round?
//
ui32Loop = (ui32Space < ui32NumBytes) ? ui32Space : ui32NumBytes;
ui32Count = ui32Loop;
//
// Update our receive counter.
//
g_ui32RxCount += ui32NumBytes;
//
// Dump a debug message.
//
DEBUG_PRINT("Received %d bytes\n", ui32NumBytes);
//
// Set up to process the characters by directly accessing the USB buffers.
//
ui32ReadIndex = (uint32_t)(pui8Data - g_pui8USBRxBuffer);
ui32WriteIndex = sTxRing.ui32WriteIndex;
while(ui32Loop)
{
UARTprintf("\n" );
//
// Copy from the receive buffer to the transmit buffer converting
// character case on the way.
//
//
// Is this a lower case character?
//
if((g_pui8USBRxBuffer[ui32ReadIndex] == 'L'))
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);
UARTprintf("LEFT" );
}
else if((g_pui8USBRxBuffer[ui32ReadIndex] == 'R'))
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
UARTprintf(" RIGHT " );
}
else
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0);
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);
//
// Is this an upper case character?
//
if((g_pui8USBRxBuffer[ui32ReadIndex] >= 'A') &&
(g_pui8USBRxBuffer[ui32ReadIndex] <= 'Z'))
{
//
// Convert to lower case and write to the transmit buffer.
//
g_pui8USBTxBuffer[ui32WriteIndex] =
(g_pui8USBRxBuffer[ui32ReadIndex] - 'Z') + 'z';
}
else
{
//
// Copy the received character to the transmit buffer.
//
g_pui8USBTxBuffer[ui32WriteIndex] =
g_pui8USBRxBuffer[ui32ReadIndex];
}
}
//
// Move to the next character taking care to adjust the pointer for
// the buffer wrap if necessary.
//
ui32WriteIndex++;
ui32WriteIndex = (ui32WriteIndex == BULK_BUFFER_SIZE) ?
0 : ui32WriteIndex;
ui32ReadIndex++;
ui32ReadIndex = (ui32ReadIndex == BULK_BUFFER_SIZE) ?
0 : ui32ReadIndex;
ui32Loop--;
}
//
// We've processed the data in place so now send the processed data
// back to the host.
//
USBBufferDataWritten(&g_sTxBuffer, ui32Count);
DEBUG_PRINT("Wrote %d bytes\n", ui32Count);
//
// We processed as much data as we can directly from the receive buffer so
// we need to return the number of bytes to allow the lower layer to
// update its read pointer appropriately.
//
return(ui32Count);
}
But I have no idea how to get the next 3 digits out of the buffer as numbers and simply write back messages to the host with a single command like UARTprintf for example.
Could you please get me on track with this?
Thanks guys,
Zoszko
I am using an MSP430 and writing code in C. I am receiving characters (working) via UART and placing them into an array rxDataArray. Since I am using an MSP430G2211, I have limited memory. The maximum array size is 50 and any more it won't build/load and says out of space.
My MSP430 is communicating to a ESP8266 module (wifi) where I am using "AT" commands. I am receive an echo of my AT command, followed by a response (ex. AT+RST responds with AT+RST...ok). I am confused using C, how I can make a string with just the "ok" response and check if it worked correctly. I have the data in the array, just not sure how to pick certain elements of the array, make a string, and compare that to "ok" response. I am used to using CString in C++ and am confused how to do this in C.
/--------------------------------------------------------------------
// Timer_A UART - Receive Interrupt Handler
//-------------------------------------------------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMERA1_VECTOR
__interrupt void Timer_A1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMERA1_VECTOR))) Timer_A1_ISR (void)
#else
#error Compiler not supported!
#endif
{
static unsigned char rxBitCnt = 8;
static unsigned char rxData = 0;
switch (__even_in_range(TAIV, TAIV_TAIFG)) { // Use calculated branching
case TAIV_TACCR1: // TACCR1 CCIFG - UART RX
TACCR1 += UART_TBIT; // Add Offset to CCRx
if (TACCTL1 & CAP) { // Capture mode = start bit edge
TACCTL1 &= ~CAP; // Switch capture to compare mode
TACCR1 += UART_TBIT_DIV_2; // Point CCRx to middle of D0
}
else {
rxData >>= 1;
if (TACCTL1 & SCCI) { // Get bit waiting in receive latch
rxData |= 0x80;
}
rxBitCnt--;
if (rxBitCnt == 0) { // All bits RXed?
rxBuffer = rxData; // Store in global variable
if (rxDataCnt < 50)
{
rxDataArray[rxDataCnt] = rxBuffer;
rxDataCnt++;
}
else
{
int i = 0;
for (i; i<50-1; i++)
{
rxDataArray[i] = rxDataArray[i+1];
}
rxDataArray[50-1] = rxBuffer;
}
rxBitCnt = 8; // Re-load bit counter
TACCTL1 |= CAP; // Switch compare to capture mode
__bic_SR_register_on_exit(LPM0_bits); // Clear LPM0 bits from 0(SR)
}
}
break;
}
}
//-----------------------------------------------------------------------
You can turn OFF echo by sending ATE0 command.
Also to find the intended string response please follow below steps:
Enable UART Transmit and Receive Interrupt.
After completing your command transmission you will start receiving data in UART ISR.
In the receive interrupt start a timer of say 1 second(You need to consider baud-rate for exact calculation).
Make track of number of received bytes in UART ISR.
Now after timer expires add null to end of last received byte in buffer.
Now,You can use string manipulation functions of C to find the intended response.
I have the following sketch, and the numTicks variable is not incrementing, the sketch compiles fine to the Arduino, but for whatever reason the variable "numTicks" is not incrementing.
/*
* kegboard-clone-4-KegCop
* This code is public domain
*
* This sketch sends a receives a multibyte String from the iPhone
* and performs functions on it.
*
* This Arduino sketch is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Public License
* along with this sketch. If not, see <http://www.gnu.org/licenses/>.
*
* Examples:
* http://arduino.cc/en/Tutorial/SerialEvent
* http://arduino.cc/en/Serial/read
* http://stackoverflow.com/questions/16532586/arduino-sketch-that-responds-to-certain-commands-how-is-it-done/
* http://davebmiller.wordpress.com/2011/01/18/arduino-flowmeter/
* http://forum.arduino.cc/index.php?topic=52003.0
* http://arduino.cc/en/Reference/AttachInterrupt
* https://github.com/just-kile/Zapfmaster2000/blob/master/src/zapfmaster2000-zapfkit-avr/draftkitAVR.ino
*
* TODO:
* - eventually get code working with the SF800 flow sensor / flowmeter
*
*/
// flow_A LED
int led = 4;
// relay_A
const int RELAY_A = A0;
// string / serial event variables
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
boolean valve_open = false;
// FLOWMETER SHIT
// flowmeter 0 pulse (input) = digital pin 2
// https://github.com/Kegbot/kegboard/blob/master/arduino/kegboard/kegboard_config.h
// which pin to use for reading the sensor? kegboard-mini shield has digital pin 2 allocated
// the SF800 outputs 5400 pulses per litre
// The hall-effect flow sensor (SF800) outputs approximately 5400 pulses per second per litre/minute of flow
// SF800 default (5400 ticks/Liter == 5.4 ticks/mL == 1/5.4 mL/tick)
int flowmeterInterrupt = 0; // changed from byte
int flowmeterPin = 2; // changed from byte
int flowmeterPinState = 0; // variable for storing state of sensor pin
// read RPM
int rpmcount = 0;
int rpm = 0;
unsigned long lastmillis = 0;
// NEW GLOBALS - 29JUL13
// initial ticks on flow meter
volatile unsigned int numTicks = 0;
// interval for flow meter frequency
int interval = 250;
volatile long previousMillis = 0;
void setup() {
// initialize serial
// Serial.flush(); // flush the serial buffer on setup.
Serial.begin(115200); // open serial port, sets data rate to 9600bps
Serial.println("Power on test");
inputString.reserve(200);
valve_open = false;
// relay for solenoid cut off valve
pinMode(RELAY_A, OUTPUT);
// flowmeter shit
pinMode(flowmeterPin, INPUT);
digitalWrite(flowmeterPin, HIGH); // Need to set these HIGH so they won't just tick away
// The Hall-effect sensor is connected to pin 2 which uses interrupt 0.
// Configured to trigger on a RISING state change (transition from HIGH
// state to LOW state)
attachInterrupt(flowmeterInterrupt, count, RISING);
}
void open_valve() {
digitalWrite(RELAY_A, HIGH); // turn RELAY_A on
valve_open = true;
}
void close_valve() {
digitalWrite(RELAY_A, LOW); // turn RELAY_A off
valve_open = false;
}
void flow_A_blink() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for one second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
void flow_A_blink_stop() {
digitalWrite(led, LOW);
}
void flow_A_on() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
}
void flow_A_off() {
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
}
// flowmeter shit
void getFlow4() {
// Serial.println("im here");
// Serial.println(sensorPin);
flowmeterPinState = digitalRead(flowmeterPin);
// Serial.println(sensorPinState);
volatile unsigned long currentMillis = millis();
// if the predefined interval has passed
if(currentMillis - previousMillis > interval) { // Uptade every 1/4 second, this will be equal to reading frecuency (Hz).
// disconnect flow meter from interrupt
detachInterrupt(flowmeterInterrupt); // Disable interrupt when calculating
// check, whether any flow was detected
if (numTicks >= 0) {
// start message to computer with tick message symbol
Serial.print("Ticks:");
// send amount of ticks for last interval
Serial.print(numTicks);
}
// clean buffer
Serial.flush();
// reset amount of ticks
numTicks = 0;
// set new start value for interval counter
previousMillis = currentMillis;
// reattach interrupt
attachInterrupt(flowmeterInterrupt, count, RISING);
}
if(flowmeterPinState == LOW) {
flow_A_off();
// Serial.println("don't blink");
}
if(flowmeterPinState == HIGH) {
flow_A_on();
// Serial.println("blink damnit");
}
if(stringComplete) {
if(inputString.equals("{close_valve}\n")) {
// Serial.println("close vavle.");
close_valve();
}
return;
}
}
// flow meter interrupt function
void count(){
numTicks++;
}
/*
* Main program loop, runs over and over repeatedly
*/
void loop() {
if(stringComplete) {
// Serial.println(inputString);
if(inputString.equals("{open_valve}\n")) {
// Serial.println("inputString equates :)");
open_valve();
}
if(inputString.equals("{close_valve}\n")) {
// Serial.println("close vavle.");
close_valve();
}
if(valve_open) {
// Serial.println("valve_open = true");
inputString = "";
stringComplete = false;
while(numTicks <= 1000) {
getFlow4();
}
}
// clear the string:
inputString = "";
stringComplete = false;
}
//Serial.println("over and over");
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
*/
void serialEvent() {
while(Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
stringComplete = true;
}
// Serial.println(inputString.length());
}
}
The reason the numTicks variable is not changed is probably due to the interrupt not triggering. You should put a breakpoint in count() to confirm this. Then you need to figure out why the interrupt isn't triggering as it should, but that is another question.
Sorry for my prior answer. I did not completely understand the problem.
First of all, are you using an Arduino UNO, if so then check the board pin labelled digital pin 2 which is mapped as "int.0" and ensure that the interrupt line from the flowmeter is connected to this pin. (see: http://arduino.cc/en/Reference/AttachInterrupt).
Per Chris' comments above, the count() routine is interrupt driven code, and it appears to be coded correctly: numTicks is defined as volatile; and count() does NOT issue I/O commands such as printf; and it does NOT return any values.
The code sample that you provide does not isolate or highlight the problem. I would write a test sketch that is just a bare bones implementation of "opening" the sensor and then sensing an interrupt from the flow meter and reporting that back to the console from the main loop. If you can get code that detects one interrupt from the flow meter to work, then add more code to report on the number of interrupts in one second, then 1/2 second, etc.
Finally, in your provided code you have the fragment:
if(valve_open) {
// Serial.println("valve_open = true");
inputString = "";
stringComplete = false;
while(numTicks <= 1000) {
getFlow4();
}
}
Since numTicks is incremented by the interrupt routine count, as a matter of principal I would NOT test it unless some of kind of serialization was implemented. getFlow4() detaches the interrupt which is one way to serialize access to numTicks. Note. in theory code can update numTicks without serialization but any value returned is not necessarily accurate as the interrupt might have fired and increased numTicks.
It seems like your application is interested in knowing the number of ticks per second?? in which case you do NOT need to test numTicks prior to stopping the interrupts. All you might need is code that once every second checks numTicks and if you can live with dropping a count then zero out numTicks without even detaching the interrupt. However, sampling numTicks is more like polling which is what an interrupt is trying to avoid.
So, since the code is keeping track of intervals then divide the interval by numTicks to get a Hz value and don't zero out numTicks or reset the interval until they get close to rolling over.
The code ended up working, it was hardware issue with the connections from the flowmeter (>.>)
You are never actually calling your count() method. You should either insert numTicks++; into the code where you wish to increase the count (recommended way), or call your count() method where you wish to increase the count. Here the count() method is only defined and not called, but it would make more sense to just increment the counter in the code since that is the only thing your defined method is doing.