Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Plasmafountain
Jun 17, 2008

2qspkL58FoHm70kEHiBT
MMLDwGT2j6QaphHj9eQu
m2QLflRyQjfhv9sth58o
qxcK9fvfbRRdNNfsywOq
O69AbmVumZOgqFeaVUWy
WtKuYLFh3UQHgjRHQoWX
faq6VQflvZbw47PwP3ih
YfMz4JYtA1c9vos24Kuu
OZ7dJFR2ojtGAZhyHrGQ
4YOcy3GrfWWwUCfazz8R

Plasmafountain fucked around with this message at 21:24 on Feb 28, 2023

Adbot
ADBOT LOVES YOU

Capntastic
Jan 13, 2005

A dog begins eating a dusty old coil of rope but there's a nail in it.

http://www.windowsondevices.com/

Microsoft is giving out Arduino-compatible boards, apparently. Not sure what the catch could be other than "please show off our product lineup!"

Plasmafountain
Jun 17, 2008

YBOw3udtIsUndzot4OzI
rncAoDdRd4LxNPlQ0Vd7
rWA8QI79CR1bLz0NKWGK
RIpkodUNvf7NgfLMs2ZF
GXAE3d1iViPE2lS74g2D
byqMU8udek2T1warmlPq
qF2r6vY80eEotV1kMyjy
FDXbURT2zc5wmomH3Uv0
PRpBMRUH9QUSxkbXMkrd
DziOdEII429Sa2Xs7hcq

Plasmafountain fucked around with this message at 21:24 on Feb 28, 2023

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
I've got a question about using the SPI port on an ATMega644 which is basically the Arduino chip with more IO lines so maybe you folks can help.

I have the SPI port set to act as master so the slave select (*SS) pin should be available as a general use pin so long as it's configured as an output, assuming I'm understanding the documentation correctly. The problem is that it doesn't seem to want to act as an output - I configure it as an output in the appropriate DDRx register and even write an initial high output state to it, but there's no positive voltage appearing on the pin. What else do I need to do to get this pin working as an output again?

Here's my SPI setup code in case it helps. The DigitalPinConfigure stuff is just my lazy way of mapping a physical pin to a port/pin# and setting the appropriate DDRx, PINx, PORTx values. It works for every other pin/port combo on the chip so I think it's OK.
code:
void SpiStart()
{	
	// set SPI select lines high
	DigitalPinConfigure(SR_SELECT, OUTPUT_HIGH);
	DigitalPinConfigure(LCD_SELECT, OUTPUT_HIGH);
	
	// set *SS, MOSI, and SCK to outputs and MISO as input
	// if *SS is not an output it will put the SPI port into slave mode when pulled low
	DigitalPinConfigure(5, OUTPUT_LOW);		// *SS
	DigitalPinConfigure(6, OUTPUT_LOW);		// MOSI
	DigitalPinConfigure(7, INPUT_WITH_PULLUP);	// MISO
	DigitalPinConfigure(8, OUTPUT_LOW);		// SCK
	
	// enable SPI system as master with a clock prescale of 4, a high SCK idle line, and data sampling on trailing clock edge.
	// SPI speed is limited by the LCD which can communicate at a max of 100kHz 
	SPCR = _BV(SPE)|_BV(MSTR)|_BV(CLKPS1)|_BV(CPOL)|_BV(CPHA);
}
e: I should mention that the _BV macro on the bottom line is basically just another way of bitshifting to the left, e.g. _BV(3) is the same as 1<<3. I don't know why they felt the need to macro that, but I'm keeping consistent with the rest of the codebase by using it.

PDP-1 fucked around with this message at 18:19 on Jul 6, 2014

mod sassinator
Dec 13, 2006
I came here to Kick Ass and Chew Bubblegum,
and I'm All out of Ass

PDP-1 posted:

I've got a question about using the SPI port on an ATMega644 which is basically the Arduino chip with more IO lines so maybe you folks can help.

I have the SPI port set to act as master so the slave select (*SS) pin should be available as a general use pin so long as it's configured as an output, assuming I'm understanding the documentation correctly. The problem is that it doesn't seem to want to act as an output - I configure it as an output in the appropriate DDRx register and even write an initial high output state to it, but there's no positive voltage appearing on the pin. What else do I need to do to get this pin working as an output again?

Here's my SPI setup code in case it helps. The DigitalPinConfigure stuff is just my lazy way of mapping a physical pin to a port/pin# and setting the appropriate DDRx, PINx, PORTx values. It works for every other pin/port combo on the chip so I think it's OK.
code:
void SpiStart()
{	
	// set SPI select lines high
	DigitalPinConfigure(SR_SELECT, OUTPUT_HIGH);
	DigitalPinConfigure(LCD_SELECT, OUTPUT_HIGH);
	
	// set *SS, MOSI, and SCK to outputs and MISO as input
	// if *SS is not an output it will put the SPI port into slave mode when pulled low
	DigitalPinConfigure(5, OUTPUT_LOW);		// *SS
	DigitalPinConfigure(6, OUTPUT_LOW);		// MOSI
	DigitalPinConfigure(7, INPUT_WITH_PULLUP);	// MISO
	DigitalPinConfigure(8, OUTPUT_LOW);		// SCK
	
	// enable SPI system as master with a clock prescale of 4, a high SCK idle line, and data sampling on trailing clock edge.
	// SPI speed is limited by the LCD which can communicate at a max of 100kHz 
	SPCR = _BV(SPE)|_BV(MSTR)|_BV(CLKPS1)|_BV(CPOL)|_BV(CPHA);
}
e: I should mention that the _BV macro on the bottom line is basically just another way of bitshifting to the left, e.g. _BV(3) is the same as 1<<3. I don't know why they felt the need to macro that, but I'm keeping consistent with the rest of the codebase by using it.

Hrm, it seems odd to me that in master mode you would be allowed to use SS as a free GPIO pin. In SPI the SS pin is held low (or sometimes high) by the master when it initiates communication to a slave device, so having free control over it doesn't seem right. I would try to use a different pin or double check the ATmega datasheet to really confirm that you can control SS when letting the ATmega handle SPI communication.

Another option, if you don't care about speed you can bit bang the SPI protocol over any GPIO pins. There are tons of examples around, but just off hand check out the spiWrite function in this LCD driver for an example: https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library

mod sassinator
Dec 13, 2006
I came here to Kick Ass and Chew Bubblegum,
and I'm All out of Ass

Zero Gravitas posted:

Well, I've solved one problem and run into another. I have my UNO board and three things to connect to it. An accelerometer, bluetooth module and a GPS unit (because I said "gently caress it, I cant use my phone output all the time").

The accelerometer uses I2C, but both the BT and GPS connect over a serial Tx/Rx connection. I also need to use (at least in the testing stage) the USB serial output to check everything is working.

From what I've read, I can only have only have one active serial connection at a time if using SoftwareSerial and while the majority of what is going on will be contained in the Arduino itself and taking measurements from the I2C accelerometer/gyroscope combo, the calculations still need periodic updating from a received signal from the serial GPS and output display data to either a PC or my phone by serial connection (either USB wired or Bluetooth).

Does anyone know of a good workaround for this? Do I have to multiplex these serial connections or something?

I think software serial would work, at least according to this page it says the only limitation is that two different things can't receive data at the same time. I would guess this is fine, especially if the devices require you to first send them something before they reply (that way you can know you'll never try to talk to both at the same time). Give it a shot to see how things work.

Plasmafountain
Jun 17, 2008

K5XRF5w2FiYZRtB44gbp
LvPbcTgoHlvoZL9fJASy
8GWuJ8CEKYfr0FTWntH9
4lNGTMlUToN5DjBeIeuj
6KJEePgIYqwEr40ksRg5
KLQlI3F12wzbxiT8EgFo
YtC9mqZlg7Q78JexEtdy
yVCWqCCWQpX57z2BfqaS
ZsUnJEoupAN0eRGYHYFf
XmRSrs96edrdNGb3ueMf

Plasmafountain fucked around with this message at 21:25 on Feb 28, 2023

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.

mod sassinator posted:

Hrm, it seems odd to me that in master mode you would be allowed to use SS as a free GPIO pin. In SPI the SS pin is held low (or sometimes high) by the master when it initiates communication to a slave device, so having free control over it doesn't seem right. I would try to use a different pin or double check the ATmega datasheet to really confirm that you can control SS when letting the ATmega handle SPI communication.

Another option, if you don't care about speed you can bit bang the SPI protocol over any GPIO pins. There are tons of examples around, but just off hand check out the spiWrite function in this LCD driver for an example: https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library

If you have multiple components connected to your SPI port you'll need more than one pin to act as SS to choose which of them you want to talk to at any given moment. Since the hardware can't know which lines you want to use for SS or which external component you want to talk to, it's up to you as the programmer to handle the SS values when the microcontroller is acting as SPI master. They turn the default SS line over to you to use as an output if you configure it as such.

Or at least that's the impression I got from this:

Atmel posted:

When the SPI is configured as a Master (MSTR in SPCR is set), the user can determine the direction of the SS pin.

If SS is configured as an output, the pin is a general output pin which does not affect the SPI system. Typically, the pin will be driving the SS pin of the SPI Slave.

If SS is configured as an input, it must be held high to ensure Master SPI operation. If the SS pin is driven low by peripheral circuitry when the SPI is configured as a Master with the SS pin defined as an input, the SPI system interprets this as another master selecting the SPI as a slave and starting to send data to it.

Zero Gravitas posted:

Ok, I'll take a crack at it. My other problem is that I need to save a calculated value in a loop to be used by the beginning of the next loop. Is there any way to do this easily or do I need to temporarily write it to the EEPROM on the chip?

Couldn't you just use a variable that is outside of whatever function contains your loop to store the result between calls? Or make a static variable within the function if you don't want other parts of the program to be able to modify it?

mod sassinator
Dec 13, 2006
I came here to Kick Ass and Chew Bubblegum,
and I'm All out of Ass
I would just put it in a global variable that's defined outside the loop and setup functions. It won't persist its value across power off/on, but in 99% of cases thats all you need. I would be hesitant to constantly write values to EEPROM since there's a finite number of writes before the EEPROM won't work anymore. IIRC its on the order of 100k writes, which in a very tight loop could go pretty quickly. Really you only need EEPROM for something that has to save state between power losses.

mod sassinator
Dec 13, 2006
I came here to Kick Ass and Chew Bubblegum,
and I'm All out of Ass

PDP-1 posted:

If you have multiple components connected to your SPI port you'll need more than one pin to act as SS to choose which of them you want to talk to at any given moment. Since the hardware can't know which lines you want to use for SS or which external component you want to talk to, it's up to you as the programmer to handle the SS values when the microcontroller is acting as SPI master. They turn the default SS line over to you to use as an output if you configure it as such.

Or at least that's the impression I got from this:

Hrm, I would compare what you're doing with what Arduino's SPI code does and see what the difference might be: https://github.com/arduino/Arduino/blob/master/libraries/SPI/SPI.cpp

Sagebrush
Feb 26, 2012

Yeah, that's pretty much exactly the situation that a global variable is intended for.

Something I've always thought about re. EEPROM was a way to get it to only write data when the power supply fails. I thought about putting a supercapacitor (~0.1F 5v) across the power leads and probing the voltage occasionally to see if it's discharging, and if it is, write any important data to EEPROM and go into a brownout state. But you couldn't just do a standard analog compare, because that compares the incoming value to the chip Vcc, right? So you'd always have the same reading. Maybe some kind of little external RC circuit powered by the supercapacitor that you sample occasionally and compare to the chip's internal clock?

I imagine this is pretty much how cars with digital dashboards write their odometer values. Or maybe they do write the value every 0.1 miles and just move from one register to the next every 50,000 writes or so? Dunno.

mod sassinator
Dec 13, 2006
I came here to Kick Ass and Chew Bubblegum,
and I'm All out of Ass
Yeah there might be different types of non-volatile RAM with longer lifetimes too. I saw Adafruit has an FRAM chip which is non-volatile and supports billions of writes: https://www.adafruit.com/product/1895 Neat little chip if you need to persist a lot of changing data.

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.

mod sassinator posted:

Hrm, I would compare what you're doing with what Arduino's SPI code does and see what the difference might be: https://github.com/arduino/Arduino/blob/master/libraries/SPI/SPI.cpp

Thanks for this. The only big difference I see is that they make their SS pin a high output before setting SPI to master mode, while I'm configuring SS as a low output. I wouldn't think that should matter but I'll give it a try next time I'm putzing around with it just to be sure.


Sagebrush posted:

Something I've always thought about re. EEPROM was a way to get it to only write data when the power supply fails. I thought about putting a supercapacitor (~0.1F 5v) across the power leads and probing the voltage occasionally to see if it's discharging, and if it is, write any important data to EEPROM and go into a brownout state. But you couldn't just do a standard analog compare, because that compares the incoming value to the chip Vcc, right? So you'd always have the same reading. Maybe some kind of little external RC circuit powered by the supercapacitor that you sample occasionally and compare to the chip's internal clock?

Google for 'Arduino brownout detector'. The chip has an on-board system for sensing low power line voltages and notifying the software when the line gets below certain levels. Unfortunately I mainly use Atmel Studio so I don't know how to access that stuff in the Arduino IDE, but it exists on the hardware side so someone has likely written a blog post or twenty about it.

e: This guy describes the process, but it looks like you need either a second arduino or an ICSP programmer unit to do it because you'll have to change some of the fuse bits.

PDP-1 fucked around with this message at 21:05 on Jul 6, 2014

Isurion
Jul 28, 2007

Capntastic posted:

http://www.windowsondevices.com/

Microsoft is giving out Arduino-compatible boards, apparently. Not sure what the catch could be other than "please show off our product lineup!"

Has anybody tried this?

Plasmafountain
Jun 17, 2008

KEPTKYCzTqBJMbPqXjhT
ZIVt5ylygEcFmdTCNG38
InCBudMfstvn8TE16I9b
NNHM0IzLrHwqxLOcsKPu
W4loEQ1pYooCDaxqPoeU
EimeimdYfc9LA2SUCYSx
Thua3gU7bJEr9l6OQgTR
5lVuC7XaRKAE8aWL2pqz
hTljJSPYwus5ZJnCIdDH
2P4coKGbuUl0uVh6xnQm

Plasmafountain fucked around with this message at 21:25 on Feb 28, 2023

Sagebrush
Feb 26, 2012

e; ^^^^^^^^^^^^ the idea is correct but the code you've written is weird. If you define a variable outside of your main loop() (or setup() ) then its value will persist until you remove power from the board.

However, what you've written, turned into proper Arduino C, is

code:
int val, newval;

void setup() {
  val = 5; // i assume this is what you meant by val = NUMBER
}

void loop {
  newval = val * analogRead(A0); //newval now contains 5* sensor reading
  val = newval; //val and newval are now the same?
  delay(50); //wait 50 ms
}
So what will actually happen is val will increase exponentially if analogRead(A0) > 1 because you're reassigning it as the multiplier. What you (probably) want is more like

code:
int last;

void loop() {
  int val = analogRead(A0);
  int avg = (last + val)/2;
  last = val;
}
in this case, last persists across multiple loops while val and avg are initialized every time, so you can use last to obtain the average of the current reading and the previous one.

I'm surprised you got this far without seeing that construct, actually -- literally the first Arduino program anyone does (blink) uses a global variable for the pin number that the LED is connected to.



Isurion posted:

Has anybody tried this?

I filled it out and they contacted me within a day to say they want to send me the dev kit. Not sure what the ETA is on that. I did email them from my professional .edu address, so that may have affected my chances of getting one.

Sagebrush fucked around with this message at 23:13 on Jul 6, 2014

Fanged Lawn Wormy
Jan 4, 2008

SQUEAK! SQUEAK! SQUEAK!

Isurion posted:

Has anybody tried this?

yeah, I put in my work email and got an email within a few minutes, also no ETA. My job also has micro controller relevance though, so ymmv. Can't hurt, right?

plasticbugs
Dec 13, 2006

Special Batman and Robin
I'm checking in after using my Arduino Yun for about a week. It has been fun, but I'm trying to do something very basic, and I'm seeing a very strange bug/out of memory issue or I'm just not understanding what's happening.

I'm using a simple Ruby script to do some work (connect to a JSON resource and return a five digit number. Then using the Arduino Bridge, I'm passing the data from the Ruby script into my sketch.

What works:
I'm able to read the numbers in and append them to a string and I'm seeing the correct string in the Serial Monitor. HOWEVER, I need to convert that string of numbers into an integer and that's where the whole thing falls apart. The string is literally "85874".

Where poo poo goes wrong:
When I convert "85874" to an integer using the string method toInt(), I wind up getting the wrong number. So, I went back to Ruby and started printing other numbers to see if low numbers could be passed in and converted to an int. And (surprise!) they can be.

I can convert any string to an integer as long as it's no higher than 32768 exactly. Now, I know that this integer is divisible by 1024, so it appears to be some kind of memory issue with the "toInt" method.

EDIT: I'm seeing that int is a 16-bit value. So that's why I'm seeing the number roll over when I try to convert it using toInt(). How the heck do I convert a large string like "90000" to a number I can perform calculations on if toInt() fails? See below for the whole problem.

:bang:
EDIT 2: I solved my problem. I needed to declare an unsigned long var. And then assign the result of toInt() to that var. Thanks for listening. Hopefully this helps someone later. I'll leave this here. In my defense, I program mostly in Ruby so I've been spoiled for the most part not having to worry about how many bits an int can hold.

The strangeness continues: If I just go into the sketch and try to convert a String that I declare as "85874" to an Int, it totally works fine. It only breaks when I try to use a string that I've created by appending chars read in from the Bridge using a Process.

I have like 3 lovely workaround in mind, but I'd rather have this work the proper way. Does anyone know why "toInt" is failing in this instance in this very particular way?

Here's some code with comments to help explain exactly what's happening (may contain typos, this is just the gist of what I'm doing):

code:

String old_subscriber_count = "";

void loop() {
  Process p;

  p.runShellCommand("ruby /usr/lib/get_initial_sub_count.rb");
  // script returns a number like "89343"

  while (p.running()); // do nothing
  
  while (p.available()) {
    char c = p.read();
    if (isDigit(c)){
      old_subscriber_count += c;
    }
  Serial.print(old_subscriber_count); // prints "89343"

  Serial.print(old_subscriber_count.toInt());
  // prints "23807", which is what you'd get if
  // you topped out at 32768, counted up to
  // 32768 again, and then finally get 23807.
  // 32768 + 32768 + 23807 = 89343

  old_subscriber_count = "";

  String test = "89343";
  Serial.print(test.toInt()); // THIS PRINTS 89343
  
  delay(60000); // wait a minute
}

plasticbugs fucked around with this message at 03:19 on Jul 7, 2014

Sagebrush
Feb 26, 2012

Yep, as soon as you said it was failing at 32768 I knew it was going to be an int problem. Glad you solved it. On the Arduino, a char is 8 bits, an int is 16 bits, and a long is 32. You can choose signed or unsigned depending on how your numbers work. This is different on other platforms; I think that by default on the desktop C will create a 32-bit integer when you initialize an int and a 64-bit one when you call a long. So it's important to know what your particular environment does.

If you want to avoid this problem in the future, you can use the literal types instead of the mnemonic ones, so that the number of bits is always right there in front of you. This will always work right regardless of the platform you port your code to:

int8_t = char (-127 to 127)
int16_t = int (-32767 to 32767)
int32_t = long (-2,147,483,648 to 2,147,483,648)
uint8_t = unsigned char (0 to 255)
uint16_t = unsigned int (0 to 65535)
uint32_t = unsigned long (0 to 4,294,967,296)

eg:
code:
uint16_t var = 255;
(one very common thing to see with the Arduino is people's timer code failing after 32.7 seconds because they used an "int" type, so after 32767 milliseconds have passed the variable rolls over. Use an unsigned long and you'll get about 48 days before you have problems)

plasticbugs posted:

In my defense, I program mostly in Ruby so I've been spoiled for the most part not having to worry about how many bits an int can hold.[/b]

Yeah, my students tell me this kind of thing too, mostly coming from Python. "Man, C is really picky about this stuff!" Yes it is! It is how a computer really works inside, not the simplified version. :P

Sagebrush fucked around with this message at 03:44 on Jul 7, 2014

Sagebrush
Feb 26, 2012

Also, I see you're using a 60,000-millisecond delay at the end to have the code wait. This will work but it's poor practice. The delay() function literally just ties up the processor until the correct time has elapsed, then continues execution. The chip is incapable of doing anything else while it's waiting for the delay to elapse. Sometimes you want this feature, for instance to have the chip ignore multiple sequential input pulses from a noisy source, but most of the time it's detrimental.

A better way is to use a timer:

code:

long t;
int wait = 60000;

void loop() {
  if (millis() > t + wait) {  //compare the current time to the last time we ran the timer loop
    t = millis();
    //do a thing
    //this code will execute every 60 seconds  
  }
  //otherwise, you can do other stuff out here in the meantime
  //checking the timer takes like 4 microseconds

}
When I explain it to my students, I compare it to sitting in front of the oven and counting "one-mississippi" until the pie is done (using delay) vs. going to do something else and occasionally looking at the clock to see if it's time to take the pie out (using a timer).

The best way is to use a timer interrupt, which is like setting an alarm to go off when the pie is done, but that can wait until later :)

Fanged Lawn Wormy
Jan 4, 2008

SQUEAK! SQUEAK! SQUEAK!

Sagebrush posted:

When I explain it to my students, I compare it to sitting in front of the oven and counting "one-mississippi" until the pie is done (using delay) vs. going to do something else and occasionally looking at the clock to see if it's time to take the pie out (using a timer).

I really like this explanation.

Also, I've been reading a lot about the Port Manipulation, but I haven't gotten around to doing any sketches with it yet. If somebody could explain the way to read an individual pin (as opposed to the whole port) using port manipulation a little more, I'd be really appreciative.

mod sassinator
Dec 13, 2006
I came here to Kick Ass and Chew Bubblegum,
and I'm All out of Ass

Fanged Lawn Wormy posted:

I really like this explanation.

Also, I've been reading a lot about the Port Manipulation, but I haven't gotten around to doing any sketches with it yet. If somebody could explain the way to read an individual pin (as opposed to the whole port) using port manipulation a little more, I'd be really appreciative.

I would stick with using the digitalRead and digitalWrite functions. Unless you're reading multiple IO pins at super fast speeds, digitalRead and digitalWrite will be much easier to use. To manipulate the ports you just directly manipulate the registers that control the pins. Behind the scenes digitalRead and digitalWrite do this manipulation, but they add some logic to map pin numbers to ports, validate input, etc. which you probably want.

All the info you need to access the ports is in the datasheet though, see section 13 on the IO system. The basic idea is that 8 bit registers define the state of the IO pins, with one bit in a register mapping to one pin. So to set a specific pin high you write a 1 to its bit in the appropriate port register. Likewise to read a pin you read the port register and check if the pin's bit is 1 or 0.

For a concrete example, say you want to read the state of digital pin 3. First you need to know which AVR pin digital pin 3 maps to, as it's not a 1:1 mapping between Arduino pin and AVR pin. From this diagram you can see digital pin 3 is actually PD3, or port D register bit 3. Luckily AVR libc exposes defines that make accessing the IO registers by name easy, so your code to read digital pin 3 would look like:

code:
// Set DDRD bit 3 to 0 to make port D bit 3 an input.
// Use boolean operators to AND the bits 11110111 with the register.
// This will force the 3rd bit to be zero because anything AND zero is zero.
// None of the other bits will be affected because one AND one is one, and
// zero AND one is zero.
DDRD &= ~0x08;
// Read PIND bit 3 to check if port D bit 3 is high or low.
// Again use boolean AND operator to check if register AND 00001000 is non-zero.
// This works because if bit 3 is high it will be one AND one which is equal to one,
// and if the bit is low it will be zero AND one which is zero.
if (PIND & 0x08) {
  Serial.println("Digital 3 is high!");
}
else {
  Serial.println("Digital 3 is low!");
}
The advantage to accessing the port registers is that you can read and write multiple pins at once. There is also slightly less overhead because there's no function call, however that kind of optimization really isn't worth worrying about unless you're in absolutely time critical code (in which case you're probably going to drop down to assembly anyways).

However as you can see there's a lot of boolean logic that you need to do, along with mapping Arduino pins to AVR IO registers and in general low level manipulation of the AVR IO registers that you need to be familiar with. Just stick with digitalRead and digitalWrite. :)

edit: Also don't be afraid to poke around the Arduino source. Here's where digitalRead and digitalWrite are implemented: https://github.com/arduino/Arduino/blob/master/hardware/arduino/cores/arduino/wiring_digital.c They are pretty simple because they call functions to map Arduino pins to the appropriate port register and bit offset.

mod sassinator fucked around with this message at 06:28 on Jul 7, 2014

TheLastManStanding
Jan 14, 2008
Mash Buttons!

Sagebrush posted:

Also, I see you're using a 60,000-millisecond delay at the end to have the code wait. This will work but it's poor practice. The delay() function literally just ties up the processor until the correct time has elapsed, then continues execution. The chip is incapable of doing anything else while it's waiting for the delay to elapse. Sometimes you want this feature, for instance to have the chip ignore multiple sequential input pulses from a noisy source, but most of the time it's detrimental.

A better way is to use a timer:

The best thing about this is that it keeps your loop times consistent (at least down to ~4 micros). When you use delays then the rest of the code can influence the loop time.

Sagebrush
Feb 26, 2012

Eh, the rest of your code can still influence it. For instance this situation:

code:
if (millis() > t + 5) {
  t = millis();
  someTimeConsumingFunction(10);
}
if the function you call takes longer than 5ms to execute (say 10ms here), that's the bottleneck in your code, and it will happen at whatever rate the function runs. Obviously this isn't a problem with the timer function, but rather with trying to do the thing more often than the chip can handle, but it's something to be aware of.

Another thing to consider:

code:
if (millis() > t + 30) {
  someTimeConsumingFunction(10);
  t = millis();
}
In this situation, your timer will run at millis() = 0, the function will run for 10ms, and at millis() = 10 we'll set t for the next run. t is 10 because we waited for the other code to run, so 10 + 30 = 40, but it should be executing at millis() = 30 because the first time was at 0.

Often this doesn't matter very much, but errors will stack up over time like crazy. Try this for instance

code:
if (millis() > t + 1000) {
  Serial.println(millis());
  t = millis();
}
The amount of time it takes to write out the time value will rapidly add errors to your time function, and you'll see a chain of numbers like this

1000
2001
3001
4002
4004
5004
6005

etc. Fractions of a percent error, but it's there.

Bad Munki
Nov 4, 2008

We're all mad here.


Also, with that timer setup, after about 50 days or whatever, everything in that if block will cease to ever run again until the arduino is reset, because t is somewhere near max long, and millis is now back to 0. It's easy enough to handle by just keeping a last_t and checking if millis() < last_t and if it is, resetting last_t (even better if you reset it but account for the delta that was left between the high last_t and max long.)

Of course that doesn't matter in probably 99.9% of the arduino projects out there, but I've run into it a few times before. For instance, an arduino that controls a keypad and some lights for unlocking a door, that'll run for many many months at a time, basically indefinitely as long as the power doesn't drop. I should probably put an uptime report on that just out of curiosity.

plasticbugs
Dec 13, 2006

Special Batman and Robin

Sagebrush posted:

Also, I see you're using a 60,000-millisecond delay at the end to have the code wait. This will work but it's poor practice. The delay() function literally just ties up the processor until the correct time has elapsed, then continues execution. The chip is incapable of doing anything else while it's waiting for the delay to elapse. Sometimes you want this feature, for instance to have the chip ignore multiple sequential input pulses from a noisy source, but most of the time it's detrimental.

A better way is to use a timer:

This is extremely helpful. Thanks for your time everyone! No pun intended. :)

Aurium
Oct 10, 2010

Bad Munki posted:

Also, with that timer setup, after about 50 days or whatever, everything in that if block will cease to ever run again until the arduino is reset, because t is somewhere near max long, and millis is now back to 0. It's easy enough to handle by just keeping a last_t and checking if millis() < last_t and if it is, resetting last_t (even better if you reset it but account for the delta that was left between the high last_t and max long.)

Of course that doesn't matter in probably 99.9% of the arduino projects out there, but I've run into it a few times before. For instance, an arduino that controls a keypad and some lights for unlocking a door, that'll run for many many months at a time, basically indefinitely as long as the power doesn't drop. I should probably put an uptime report on that just out of curiosity.

There's a simple way of doing timers that's completely immune to overflow. No fancy overflow handling or detection.

code:
unsigned long LastTime = 0;

void loop() {
    if (millis() - LastTime >= HowMuchTimeToWait) { //or micros()
        LastTime = millis();
        //do stuff you want to do here
    }
}
Note that it is important that LastTime is unsigned. This works due to how unsigned math works.

As I noted above this also works with micros() which overflows every ~70minutes. So if you need something with a higher resolution than millis, it's very convenient to be able to use micros without worrying about the constant overflows.

You can reduce timing errors even a bit more with this variant:
code:
unsigned long LastTime = 0;

void loop() {
    unsigned long CurrentTime = millis();
    if (CurrentTime - LastTime >= HowMuchTimeToWait) { //or micros()
        LastTime = CurrentTime;
       //do stuff you want to do here
    }
}
If you ever forget the exact structure of this method you can look at the BlinkWithoutDelay example. Alas it also included two simple bugs, the first is that they forgot to to make all of the longs unsigned, which will cause bad behavior. The second is that they are using > instead of >=, which will cause you to be off by a millisecond or microsecond. Much more minor.

quote:

The advantage to accessing the port registers is that you can read and write multiple pins at once. There is also slightly less overhead because there's no function call, however that kind of optimization really isn't worth worrying about unless you're in absolutely time critical code (in which case you're probably going to drop down to assembly anyways).

I don't really agree with the parenthesized comment. Just port manipulation alone is an excellent low hanging fruit when you need a speed boost when you're doing high volume io. Most C can be pretty well optimized by the compiler, but the digitalwrite and read calls can't be. I was playing with some led sign panels (multiplexed 16x32 tricolor arrays, usually driven by a fpga.) and just changing out digital write to direct port manipulation took me from tons of flickering to completely smooth. And I didn't have to learn assembly.

Check out mike's electric stuff for him playing around with some very similar panels. I didn't get dimming working, but I'm not sure that's even possible on a 16mhz chip. Maybe 4 levels of intensity.

Now port manipulation is around 100x faster, but it really helped because they were tight loops that were no more than read memory, write 3 bits (color, that could be done in parallel), and then 2 more separately for the clock.

So really the important question is how often are you io limited?

Mr. Bubbles
Jul 19, 2012
I'm just getting started with Arduino and have a couple of questions on a project. I currently have a RPi that drives a servo via a web control with python / flask. I would like to make the RPi wirelessly communicate with an Arduino to execute a command that drives the servo, so that the servo is not physically connected to the pi. I have two nrf24L01+ modules that I was planning to use, but am having trouble understanding how to use the libraries to establish such an RF link.

- Would I be better off using xbee or another RF mechanism for communication? It seems that xbee is more commonly used and may be better documented for me to learn.
- If I can stick with the nrf24L01+ modules (one with the pi, one with the arduino), could anyone point me to a tutorial or documentation that may help me figure out the coding to use the module?

Thanks!

Economic Sinkhole
Mar 14, 2002
Pillbug
My dog bark detector/counter project is fairly successful so far, including tweeting the barks. https://twitter.com/barkbarkbarkb I ended up using an Uno and monitoring the voltage on the LED using an analog pin. The Pi runs a python script that continually monitors the serial connection and tweets when the detector hears a bark. I am thinking about trying to add audio recording in to the mix somehow, so I can confirm that it is actual barks that are setting the detector off and not other noises.

It has been a pretty fun process learning this stuff, I am excited to learn more about what I can do with Arduinos.

sharkytm
Oct 9, 2003

Ba

By

Sharkytm doot doo do doot do doo


Fallen Rib

Economic Sinkhole posted:

My dog bark detector/counter project is fairly successful so far, including tweeting the barks. https://twitter.com/barkbarkbarkb I ended up using an Uno and monitoring the voltage on the LED using an analog pin. The Pi runs a python script that continually monitors the serial connection and tweets when the detector hears a bark. I am thinking about trying to add audio recording in to the mix somehow, so I can confirm that it is actual barks that are setting the detector off and not other noises.

It has been a pretty fun process learning this stuff, I am excited to learn more about what I can do with Arduinos.

I'll take a long, deep bow, then walk off stage without saying anything. :drops mic:

That's loving awesome. BARKBARKBARKB!

Plasmafountain
Jun 17, 2008

MzyipZx9HK0b9G9OyusJ
1FInlIVrDuUyiZE1ODSK
w2W9ULdIE2sNV5EhBorN
p2paTHwSmiLlrBjzwiDe
Q98TuFuP42hMNiCDZP3w
87RUmzovX9iTMIfOThuL
mmJarLxCUxhbgHSEkIeQ
aZTi7w5cV0MSuKdyVYL9
mL8g9D4lQoFwS5z0CTtN
yQcmzLzVARdXZxO121gn

Plasmafountain fucked around with this message at 21:25 on Feb 28, 2023

Sagebrush
Feb 26, 2012

If you wanna get in the newspaper (probably also arrested), what you need to do now is build a little solar-powered device with a GSM modem and a speaker that scrapes that twitter account and plays a dog barking noise every time it happens, then stick that on a lamppost next to city hall.

porksmash
Sep 30, 2008
You should sum up barks per minute and tweet "BARK" * occurences. "BARKBARKBARK". Hopefully, for your sake and technical limitations, there are less than 30 barks per minute.

Mantle
May 15, 2004

My company is going to sponsor me to go to an LED strip workshop controlled by Arduino and I want to build an infinity mirror for my project. Has anyone in this thread built one and can you tell me what you wish you had done differently?

I'm fairly comfortable with the electronics and software side of it but I'm pretty newb at building physical things.

Sagebrush
Feb 26, 2012

One of my students *just* finished building one for his Arduino project, and I can tell you it is a good idea to not cut mirrored (metalized) acrylic on a table saw that has an automatic finger-detection brake that works on capacitive principles.

poeticoddity
Jan 14, 2007
"How nice - to feel nothing and still get full credit for being alive." - Kurt Vonnegut Jr. - Slaughterhouse Five

Sagebrush posted:

One of my students *just* finished building one for his Arduino project, and I can tell you it is a good idea to not cut mirrored (metalized) acrylic on a table saw that has an automatic finger-detection brake that works on capacitive principles.

Do you have any info about the automatic finger-detection brake, because that sounds really cool and I'd like to know how it works. :science:

Sagebrush
Feb 26, 2012

I used to think it was like a GFCI, but it turns out it's more like a capacitive touchscreen: the blade is connected to a detection circuit that calibrates when you start it up, and then if you touch it while it's spinning with anything that has a significant capacitance of its own (like the 100+ pounds of water in a human body) it triggers the brake.

The brake itself is the neat part -- it's a chunk of aluminum hovering right below the blade, connected to a powerful spring, a thin piece of wire and a big capacitor. When you start up the table saw the capacitor charges. Triggering the brake discharges the capacitor into the wire, exploding it and releasing the brake to be forced upwards by the spring. The sawblade embeds itself into the brake cartridge, which is hinged, so the inertia of the blade pushes it downwards into the table where it locks in place. The whole thing happens in something like 5 milliseconds.

https://www.youtube.com/watch?v=qssfzp_KqBI

Ours has saved more than one finger in its time. The worst injury I've seen was from a guy who, through his own carelessness, managed to ram his thumb directly into the front of the spinning blade with some significant force. He would have lost his finger on any other table saw; on this machine he got an eighth-inch wide cut a millimeter deep. People touching it more gently, like brushing the edge while slowly pushing forwards like you're supposed to, barely get a scratch. It's really phenomenal technology and if I were in the market for a table saw I wouldn't be considering anything else.

Of course, anything that is electrically connected to something with a large capacitance (say, a piece of water-logged wood, or a piece of mirrored acrylic that you're touching the edge of) will also trigger the brake. They aren't reusable.

e: the old versions of the brake system used a .45 blank cartridge instead of a spring and a capacitor, which is pretty :black101:

Sagebrush fucked around with this message at 02:17 on Jul 12, 2014

JawnV6
Jul 4, 2004

So hot ...

poeticoddity posted:

Do you have any info about the automatic finger-detection brake, because that sounds really cool and I'd like to know how it works. :science:

http://en.m.wikipedia.org/wiki/SawStop

Dude went on letterman back in the day to show it off.

echinopsis
Apr 13, 2004

by Fluffdaddy
The Pro-Mini seems to have half the ram of an UNO.. Is that referring to the memory my program goes into or working memory?

Adbot
ADBOT LOVES YOU

Sagebrush
Feb 26, 2012

There are a lot of Pro Minis with different chips on them. Do you have a link to the specific product you're talking about?

That said, if it specifically says "RAM" or "SRAM" it means working memory. Program space will be called "program space" or "flash" or something and the chip number gives you a hint (ATMega328 = 32k, ATMega168 = 16k, ATTiny84 = 8k, etc).

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply