Consider the following loop in C:
int i;
for (i = 1; i > 0; i++);
After i reaches INT_MAX, it will turn INT_MIN at increment. Then i > 0 evaluates
false, and the for loop will terminate.
However, the same for loop will not terminate in Arduino, since i > 0 compares false
even if i is -32768.
Why is that?
PS. I'm using a Mega 2560 with Arduino version 1.0.5.
PPS. int is indeed 16 bit on Mega 2560
PPPS. The complete sketch below:
void setup () {
int i;
Serial.begin(9600);
for (i = 1; i > 0; i++);
}
void loop () {
Serial.println(100);
}
And I won't see any thing in the Serial Monitor
Disassembling the setup function gives:
0000014a <setup>:
14a: 80 e3 ldi r24, 0x30 ; 48
14c: 94 e0 ldi r25, 0x04 ; 4
14e: 40 e8 ldi r20, 0x80 ; 128
150: 55 e2 ldi r21, 0x25 ; 37
152: 60 e0 ldi r22, 0x00 ; 0
154: 70 e0 ldi r23, 0x00 ; 0
156: 42 d2 rcall .+1156 ; 0x5dc <_ZN14HardwareSerial5beginEm>
158: ff cf rjmp .-2 ; 0x158 <setup+0xe>
so now it's clear: avr-gcc thinks the i > 0 in for(i = 1; i > 0; i++) will never evaluates false and optimised the exit condition away.
Most likely, 'int' is also 32-bit on Arduino. It takes time to complete 2^31 iterations. You may change 'int' to 'short' and it should terminate as expected.
when int is defined, by default, its size is assumed to be of 16 bit by compiler.
But when you increment it beyond 32767 i.e.
int i=32767;
i++;
The size of 'i' is automatically increased to 32 bit. This is because the size is not specified in the declaration.
This causes your program not to stop at 32767.
Hence if you want to stop the for loop at 32767,
you have to specifically declare it as
short int i;
If you want to verify this, you can try following
short int x;
for(x=1; x>0; x++)
{
mySerial.println(x);
}
AND
int x;
for(x=1; x>0; x++)
{
mySerial.println(x);
}
Related
I am working on a project where I try to code my own function for a membrane keypad. I want the 16 key states to be stored in a uint16_t variable, so I have only one variable. The code compiles.
The problem is that it will not properly display the key states. When I press the 1 key, it will tell me a lot more keys are being pressed. It will also display that keys are being pressed without touching the thing at all.
Pins 5-8 of the keypad are connected to PORTD, pin 4-7 on the Nano.
Pins 1-4 of the keypad are connected to PORTB, pin 0-3 on the Nano.
Keypad [PDF].
This is the code.
uint16_t keys = 0;
void setup() {
// put your setup code here, to run once:
DDRD |= 0xF0; //Set bit 4-7 as output on PORTD
DDRB &= 0xF0; //Set bit 0-3 as input on PORTB
Serial.begin(9600);
}
void loop() {
getKeys(&keys);
Serial.println("-----------------------");
delay(100);
}
void getKeys(uint16_t* keys){
*keys = 0; //Clear keys variable
for(int rows = 0; rows < 4; rows++){ //Loop through every row
PORTD &= 0x0F; //Turn pin 4-7 on PORTD off
PORTD |= (16 << rows); //Turn on pin.
Serial.print(String(PORTD) + String(" : ")); //Print current selected bit(row)
uint16_t temp = (PINB & 0x000F); //Take bit 0-3 from PORTB
Serial.println(temp, BIN); //Print the PINB values as binary
*keys |= (temp << (rows*4)); //Shift the PORTB values into keys.
}
}
This is the output in the Serial monitor.
16 : 0
32 : 0
64 : 0
128 : 0
-----------------------
16 : 0
32 : 0
64 : 0
128 : 1
-----------------------
16 : 0
32 : 0
64 : 0
128 : 11
-----------------------
16 : 0
32 : 1000
64 : 10
128 : 1111
-----------------------
16 : 1000
32 : 1110
64 : 1110
128 : 1111
-----------------------
16 : 1000
32 : 1110
64 : 1110
128 : 1111
-----------------------
16 : 0
32 : 0
64 : 0
128 : 0
-----------------------
16 : 0
32 : 0
64 : 0
128 : 0
-----------------------
When you have all keys unpressed, then you inputs are not connected to anything. They just are "floating in the air" and can receive any electrical disturbance from outside.
Keypad should work in other way.
On input pins (PINB) turn on pull-up resistors DDRB &= 0xF0; PORTB |= 0x0F. Then ALL you inputs now should read as logical 1, eg. 1111.
On output pins (PORTD) provide HIGH level (logical 1) to all rows, except the one, that you are going to measure (this should be LOW level - logical 0), e.g. 1110, 1101, 1011, 0111.
Then you can read pressed key as LOGICAL 0 on input pins (PINB). After that, you can invert the read number, if you need (bitwise NOT operator ~), e.g. temp = PINB; temp ~= temp; temp &= 0x0F or just temp = (~PINB) & 0x0F.
The same way you can use bitwise NOT opearator before output, e.g. PORTD = (~(16 << rows)) & 0x0F.
This way your inputs on PINB will always be connected to some source - to VCC (+5V) through pull-up resistors inside the AVR chip, or to GND, when you set logical 0 on output pins of PORTD. And you will not receive any electrical noise on them.
I'm using the Arduino IDE to program a ESP8266 board.
The idea is to play a sound of a random frequency for a random amount of time, and save the last frequency played to the EEPROM. Then, I have a watchdog ISR that restarts the board if the delay time is 4 seconds or more. When this happens and the board restarts, I want to play the last played frequency for 1 second, and then resume normal functionality again.
Here is my code,
#include <Ticker.h>
#include <EEPROM.h>
#define PIN_BUZZER 13 // the digital pin the Buzzer is attached to
PROGMEM const int freqs[] = {31, 49, 78, 123, 196, 311, 494, 784, 1245, 1976, 3136,
4978};
Ticker secondTick;
volatile int watchdogCount = 0;
volatile int freqIdx = 0; //the index that will store the last frequency before it restarts
int EEPROM_Addr = 42;
//The Watchdog Interrupt Service Routine (ISR)
void ISRwatchdog() {
watchdogCount++;
//The watchdog will be waken up when the couter reaches 4
if (watchdogCount == 4) {
ESP.restart(); //restarting the board
}
}
void setup() {
EEPROM.begin(4096);
Serial.begin(115200);
secondTick.attach(1, ISRwatchdog); //registering the watchdog ISR
pinMode(PIN_BUZZER, OUTPUT);
int prevFreq = EEPROM.read(EEPROM_Addr); // read previous frequency
if (prevFreq != 255){
Serial.println("Previous frequency found : ");
Serial.println(prevFreq);
analogWrite(PIN_BUZZER, 256);
analogWriteFreq(prevFreq);
delay(1000);
}
}
void loop() {
Serial.print("Watchdog counter = ");
Serial.println(watchdogCount);
watchdogCount = 0;
int freq = freqs[random(0, 11)];
Serial.print("Frequency: ");
Serial.println(freq);
Serial.println("Saving to EEPROM");
EEPROM.write(EEPROM_Addr, freq);
EEPROM.commit();
// generating 50% PWM
analogWrite(PIN_BUZZER, 256);
analogWriteFreq(freq);
//depending on the value of delay, the program may wake up the watchdog
int delayTime = random(1, 5) * 1000;
Serial.print("Delay time: ");
Serial.println(delayTime);
delay(delayTime);
}
The problem I'm facing is that values are either written to the EEPROM incorrectly or they are being read incorrectly. For example, here is some of the output I got,
Watchdog counter = 2
Frequency: 31
Saving to EEPROM
Delay time: 3000
Watchdog counter = 3
Frequency: 1245
Saving to EEPROM
Delay time: 4000
ets Jan 8 2013,rst cause:2, boot mode:(3,0)
load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v09f0c112
~ld
Previous frequency found :
221
The previous frequency in this case is incorrect.
And again in this output snippet,
Watchdog counter = 1
Frequency: 784
Saving to EEPROM
Delay time: 4000
Watchdog counter = 4
Frequency: 1976
Saving to EEPROM
Delay time: 1000
ets Jan 8 2013,rst cause:2, boot mode:(3,0)
load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v09f0c112
~ld
Previous frequency found :
184
There are some cases where my output is correct, but that is rare.
The EEPROM library for ESP8266 only stores one byte. That means it cannot store values over 255 -- the value you get back is least significant byte of the value you stored (i.e, freq % 256).
Consider storing the index of the value in the array instead of the value itself, e.g.
uint8_t idx = random(0, 11);
int freq = freqs[idx];
...
EEPROM.write(EEPROM_addr, idx);
and
uint8_t idx = EEPROM.read(EEPROM_addr);
int freq = freqs[idx];
How can I get the serial number printed on an RFID tag through an RFID reader?
I have:
Arduino uno,
RMD 6300 reader, and
RFID tag (125Khz).
I use the following code:
#include <SoftwareSerial.h>
SoftwareSerial RFID(2, 3); // RX and TX
int i;
void setup()
{
RFID.begin(9600); // start serial to RFID reader
Serial.begin(9600); // start serial to PC
}
void loop()
{
if (RFID.available() > 0)
{
i = RFID.read();
Serial.print(i, DEC);
Serial.print(" ");
}
}
I get this value:
2 48 57 48 48 50 69 52 69 65 50 67 66 3
But the following value is printed on the RFID tag:
0003034786
I would want to have this number, but I don't know how to convert it.
The value that you currently get is the serial number encoded as US-ASCII string. The value in decimal (as you currently print it) is
2 48 57 48 48 50 69 52 69 65 50 67 66 3
Converting these bytes into hexadecimal form (for better readability) leads to:
02 30 39 30 30 32 45 34 45 41 32 43 42 03
Encoding these bytes in US-ASCII leads to this string:
<STX>09002E4EA2CB<ETX>
Note that you could also receive this form directly on your console by using
Serial.write(i);
instead of Serial.print(i, DEC);
Thus, your reader starts outputting the serial number by sending a start-of-transmission (STX) character (0x02) and ends sending the serial number with an end-of-transmission (ETX) character. Everything in between is the serial number (represented as hexadecimal characters):
09002E4EA2CB
The serial number printed on your key (0003034786) is only a fraction of the complete serial number. This value is the decimal representation. If you convert
0003034786
to its hexadecimal representation, you get
002E4EA2
This value is contained in the serial number that you received from the reader:
09002E4EA2CB
Therefore, you could do something like this to print the value (use sprintf(), if you need the leading zeros):
void loop() {
int serialNumber = 0;
int charIndex = 0;
int currentChar;
if (RFID.available() > 0) {
currentChar = RFID.read();
++charIndex;
if (currentChar == 0x002) {
charIndex = 0;
serialNumber = 0;
} else if (currentChar == 0x003) {
Serial.print(serialNumber, DEC);
Serial.print(" ");
} else {
if ((charIndex >= 1) && (charIndex < 5)) {
serialNumber <<= 8;
serialNumber += currentChar;
}
}
}
}
How do I read pin 1-6 and show the value on Arduino TFT display. (BIN to BCD)
Ex: pin 5,4,3,2,1,0 all = 1 (111111) will show the value on display: 63
Ex: pin 5,4,3,2,1,0 (100000) will show the value on display: 32
Ex: pin 5,4,3,2,1,0 all = 0 (000000) will show the value on display: 00
Something along these lines:
int result = 0;
for (int i = 0; i < 6; i++)
if (digitalRead (i + 1) == HIGH)
result |= bit (i);
if (result < 10)
Serial.print ("0");
Serial.println (result);
That "ors" in each bit from pins 1 to 6. I used a Serial print there because you didn't give any indication what sort of TFT display you have*, but the principle will be the same.
* Other than being an Arduino one. :)
I want to get the MAC address of xbee, but I'm not succeeding.
I have the following code.
uint8_t myaddress[10];
uint8_t shCmd[] = {'S','H'};
uint8_t slCmd[] = {'S','L'};
AtCommandRequest atRequestSH = AtCommandRequest(shCmd);
AtCommandRequest atRequestSL = AtCommandRequest(slCmd);
AtCommandResponse atResponse = AtCommandResponse();
void getMyAddress(){
xbee.send(atRequestSH);
if(xbee.readPacket(5000)){
if (xbee.getResponse().getApiId() == AT_COMMAND_RESPONSE) {
xbee.getResponse().getAtCommandResponse(atResponse);
if (atResponse.isOk()){
for(int i = 0; i < atResponse.getValueLength(); i++){
myaddress[i] = atResponse.getValue()[i];
}
}
}
}
delay(1000);
xbee.send(atRequestSL);
if(xbee.readPacket(5000)){
if (xbee.getResponse().getApiId() == AT_COMMAND_RESPONSE) {
xbee.getResponse().getAtCommandResponse(atResponse);
if (atResponse.isOk()){
for(int i = 0; i < atResponse.getValueLength(); i++){
myaddress[i+6] = atResponse.getValue()[i];
}
}
}
}
}
I hoped that the myaddress array were 10 values, because the Xbee MAC address contains 64 bytes.
But the array contains only 8 values, for example:
Original Xbee Address is 0013a200408a31bb
Result function getMyAddress is 013a20408a31bb
My function loses two zeros.
I print the MAC address with the following code:
for(int i=0; i < 10; i++)
Serial.print(myaddress[i], HEX);
Any ideas?
The problem is your function does not print a leading zero if the number is less than 10.
With spaces between each byte: 00 13 a2 00 40 8a 31 bb
What you are printing with spaces: 0 13 a2 0 40 8a 31 bb
I don't think there is a simple way to print hex values with a leading zero, but you can change how you print to have a space between each byte:
for(int i=0; i < 10; i++) {
Serial.print(myaddress[i], HEX);
Serial.print(" ");
}
The MAC address is 64 bits, which is 8 bytes (64 bits / (8 bits/byte)). ATSH and ATSL both respond with a 4-byte value. So you should define my address as 8 bytes, and copy ATSL to myaddress[i+4].
Note that you can use memcpy() instead of looping through the bytes:
memcpy( &myaddress[i+4], atResponse.getValue(), 4);
I'm not familiar with the Arudino's Serial.print(), but if it doesn't support printing a hex byte with leading zero, you can print the MAC with:
for (int i = 0; i < 8; i++) {
if (myaddress[i] < 0x10) Serial.print( "0");
Serial.print( myaddress[i], HEX);
}