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
PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
I have a Duemilanove/168 and seem to be having an issue with an interplay between the serial port and Timer0.

I'm trying to set up a watchdog timer that counts down over the course of ~1 second, disables the unit via setting a boolean to false if the watchdog gets to zero, or keeps the unit enabled by resetting the watchdog counter if a 'heartbeat' command is detected. The idea is that the control PC will send heartbeat commands periodically to keep the unit enabled during normal operation, but if communication is lost for any reason the Arduino-controlled system should shut itself down.

The problem I'm seeing is that whenever I open or close the serial port from the control computer the watchdog timer gets reset even when no heartbeat command (or data of any other kind) is sent. This is mysterious since calling HeartBeatCommand() should be the only way to reset the watchdog, and as far as I can tell (by making that command turn on the on-board LED) it's never getting called. It's as if just opening/closing the serial port resets heartbeatWatchdogCounter to 0xFF, and the output pin (pin2) goes high for ~1 second.

Any ideas as to what could be going on? My only guess is that something in the Arduino's serial com system is messing with my ISR for that timer, but that still wouldn't explain why the watchdog counter gets reset.

semi-edit: While composing this post I decided that initializing heartbeatWatchdogCounter to zero was better than initializing to 0xFF, and that one change seems to have fixed the spurious resets caused by opening/closing the serial port on the control PC. I can't understand how that would matter, but it's some kind of clue at least.

C++ code:
// varables
const int HEARTBEAT_PIN = 2;
byte heartbeatWatchdogCounter = 0xFF;
boolean heartbeatEnable = false;

// call this when a heartbeat command is sent from the control computer to reset watchdog counter
// and echo back the command ('H')
void HeartBeatCommand()
{
  heartbeatWatchdogCounter = 0xFF;
  Serial.println(HEARTBEAT_COMMAND);
}

// interrupt service routine for Timer0, this fires about 200 times per second.
ISR(TIMER0_COMPA_vect)
{
  if(heartbeatWatchdogCounter>0) {--heartbeatWatchdogCounter; }
  heartbeatEnable = (heartbeatWatchdogCounter>0);
  digitalWrite(HEARTBEAT_PIN, heartbeatEnable);
}

Adbot
ADBOT LOVES YOU

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
Fair enough. The simplified code below seems to reproduce the problem, opening or closing the Arduino IDE serial monitor window (ctrl+shift+M) opens the serial port and appears to reset the watchdog timer and makes pin2 high for ~1 second.

It also appears to reproduce the issue where initializing heartbeatWatchdogCounter to 0x00 makes the serial port problem go away, while initializing it to 0xFF brings the serial port problem back. This is using the Arduino 1.0.3 IDE if that matters.

e: Just checked with the latest version (1.0.4) of the IDE and got the same result.

C++ code:
const int HEARTBEAT_PIN = 2;
const char HEARTBEAT_COMMAND = 'H';
int heartbeatWatchdogCounter = 0xFF;
boolean heartbeatEnable = false;

void setup()
{
  Serial.begin(9600);
  pinMode(HEARTBEAT_PIN, OUTPUT);
  HeartbeatTimerSetup();
}

void loop()
{
  while(Serial.available()>0)
  {
    char input = Serial.read();
    if(input == HEARTBEAT_COMMAND)
    {
      HeartbeatCommand();
    }
  }
}

void HeartbeatCommand()
{
  heartbeatWatchdogCounter = 0xFF;
}

void HeartbeatTimerSetup()
{
  //set up Timer0 registers
  TCCR0A = 0;
  TCCR0B = 0;
  TCNT0  = 0;
  
  // set compare match register 
  OCR0A = 0xFF;
  
  // turn on CTC mode
  TCCR0A |= (1 << WGM01);
  
  // set prescaler level
  TCCR0B |= (1 << CS12)|(1 << CS10);
  
  // enable timer compare interrupt
  TIMSK0 |= (1 << OCIE0A);
}

// Timer0 ISR
ISR(TIMER0_COMPA_vect)
{
  if(heartbeatWatchdogCounter>0) {--heartbeatWatchdogCounter; }
  heartbeatEnable = (heartbeatWatchdogCounter>0);
  digitalWrite(HEARTBEAT_PIN, heartbeatEnable);
}

PDP-1 fucked around with this message at 16:20 on Apr 21, 2013

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.

TheLastManStanding posted:

Opening the serial monitor resets the arduino (in the same way that pressing the reset button does) which is why your value and pin state changes briefly. A quick google search led to an arduino playground discussion on disabling the auto reset.

Bingo! That was the problem, and explains all of the symptoms I was seeing. Thanks so much for this - I was totally barking up the wrong tree thinking that the serial port was mucking with timer settings, and was thinking about dumping the ROM and searching for anything that touched the Timer0 registers manually. Instead it turned out to be something really simple. :)

evensevenone posted:

Also having the device spit out "reset" on startup will save you a ton of grief.

Also good advice, it helped confirm that the device really was being reset upon starting the serial monitor window.


Thanks again thread!

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

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?

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

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
How about using a gearbox/stepper motor combo? Choose a gearbox that'd give you the right amount of torque and mount it on the valve stem, then drive the stepper motor off the Arduino via an appropriate motor driver board for whatever number of steps you determine to be sufficient to turn it 90 degrees. A proper solenoid valve would be my first go-to, but you know your setup better than I do.

For the lightswitch, the SSR in parallel with the existing switch that Bad Munki mentioned would be simple and reliable.

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.

An alternate method would be to set up one of the timers to count clock cycles with an appropriate prescaler to form a clock that 'ticks' once per 0.1 second. Each 'tick' would fire off an interrupt that would increment some counter variable by one. At the start of your balloon filling step you'd zero out that variable and then in the loop() function you'd check it to see if it had grown beyond some value representing a reasonable amount of time needed to fill the balloon. If the tick counter indicates that the balloon hasn't filled within a safe timeframe you could shut the physical system down to a safe state.

If you don't really care about a possible bug every 49 days then just use millis() because it's way easier. If you do care about running for a long time this method would get around the 2^32 overflow problem.

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
I've never actually tried doing this, but it seems like it'd be possible to 'overdrive' the servo a bit during long moves by adding your own proportional control in code. Something like this:

code:
int SV;      // our setpoint variable, aka where we want to go
int PV;      // our process variable, aka where the servo actually is
int SV2;     // our 'overdrive' setpoint variable
int upperBound, lowerBound;  // the upper and lower bounds of the range we want the servo to move through
float P;   // a proportional control term that we'll have to experiment to find the right value for

while(1)
{
     PV = readActualServoPositionHere();
     SV2 = SV + (int)(P * (SV - PV));                       
     if(SV2>upperBound){ SV = upperbound; }
     if(SV2<lowerBound){ SV = lowerBound; }
     writeNewServoDriveValueHere(SV2);  
}
Just to game out how that would work, suppose our proportional constant P is 1.0f, we are starting at 0% rotation and want to go to 50% rotation. On the first iteration of the loop the overdriven setpoint is SV2 = 50% + 1.0f * (50% - 0%) or SV2=100%. This means that we're telling the servo that it's way off the mark and it should move really fast.

Now suppose the servo has turned for a bit and is at 45% rotation. The new overdriven setpoint is SV2 = 50% + 1.0f * (50% - 45%) or SV2 = 55%. In other words, as the servo approaches its final position the overdrive variable starts to slack off a bit. It's easy to see that when SV=PV, aka we're exactly where we want to be, SV2=SV so the overdrive isn't doing anything anymore.

Basically we're adding our own proportional control loop term to the servo's internal control loop to speed it up when it's far from the desired position. To find the appropriate value for P run the servo under normal load conditions and gradually increase P until the servo starts to oscillate around the setpoint position. Throttle P back to about 70% of the minimum P value that causes oscillations.

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
I'm trying to configure the INT0 interrupt to act like a one-shot edge detector that will monitor some signal IN_PIN for an upward transition and then briefly pulse OUT_PIN before going back to being quiet for a while.

I stripped down my code to what is shown below, where I periodically enable INT0 in the main loop and disable it in its' own ISR to make it a one-shot.

code:
#define IN_PIN 2    // external interrupt INT0 input
#define OUT_PIN 6   // gpio pin output

void setup() {
  pinMode(IN_PIN, INPUT);
  pinMode(OUT_PIN, OUTPUT);
  digitalWrite(OUT_PIN, LOW);
  EICRA = (1 << ISC01) | (1 << ISC00);  // fire INT0 on rising edge
}

void loop() {
  delay(10);          // simulate waiting for PC to enable interrupt
  EIFR = 0;           // clear any existing interrupt flags
  EIMSK = (1<<INT0);  // enable INT0 interrupt
}

ISR(INT0_vect) {
  digitalWrite(OUT_PIN, HIGH);// pulse output to show interrupt happened
  digitalWrite(OUT_PIN, LOW);
  EIMSK = 0;            // disable INT0 interrupt (this causes problems?)
}
The problem is that when I do this the phase of OUT_PIN doesn't seem to be connected to the upward edge transition of IN_PIN:



As I was messing around with the code I noticed that removing the final EIMSK=0 line in the ISR caused the output pulses to line up with the positive edge transitions of the input pulses, but of course now we loose the triggerable one-shot behavior that I was hoping for:



Any ideas as to what causes setting EMISK=0 to make INT0 go crazy like this? A also tried more carefully making the bits I wanted to change in case some of the reserved bits in that register were connected to something important, aka EMISK&=0xFC but it didn't change anything.

e: less table-breaking images

PDP-1 fucked around with this message at 19:29 on Apr 19, 2016

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.

JawnV6 posted:

If you're comfortable enough using EIMSK registers, maybe you could skip using digitalWrite and send values directly to the pin registers instead. Also, put another (lightweight) pin write in the loop and toggle it when you're re-enabling INT0. Pull that up on the scope and see when that's occurring.

Thanks for the comments. I added a second output pin to show when INT0 was getting enabled and it revealed that INT0 was firing off instantly after being turned on.

I read up on how interrupts are generated and found this:



So basically you have to write a 1 to the flag bits to set them to 0, as clearly spelled out on one line buried in the 660 page datasheet. I had been writing a 0 to EIFR to make it 0 like some kind of goddamn fool. :downs:

Anyway, after making that one change everything is working great and one-shotting like I wanted with pretty tight timing.

Captain Cool posted:

EIFR bits are cleared by writing 1, not 0. It would be strange if that fixed your problem, but it's worth a shot.

See, this guy read the 660 page manual cover-to-cover. :)

It actually did fix the issue, and there doesn't seem to be much of a set-up time needed according to my scope - maybe a handful of clock cycles to finish whatever instruction is currently being processed and jump to the ISR vector.

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
Yeah, shift registers like the xx595 can be hooked up directly to a SPI port and then chained in series for as many input/output ports as you could practically want. I did this for an old project where I was switching a bunch of relays to drive lights on a UI:



The data in pin connects to the SPI MOSI, the shift clock is the SPI SCK signal, and the latch pin is the SPI SS*. If you want to run more than 8 bits of output, just run the data out of register #1 into the data in of register #2, etc., and tie their latch lines together.

In operation you'd bring the SS line low, write N bytes out your spi port for the N registers you have chained together, then bring the SS line high and they'd all latch their new data to their outputs simultaneously.



*or whatever digital output you want to use as a chip select signal

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.

xilni posted:

Anyone know a better way to breadboard an esp8266 than this?



Get something like these, break off an appropriate number of pins, solder the short end to the pcb and stick the long end in the breadboard?

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
You could check out the MAX31855 family of chips and use a thermocouple as the sense element. The MAX chip connects directly to the thermocouple and delivers its readings over a SPI connection. You can get Type-K thermocouples inside of stainless steel probes for less than $10USD off Amazon.

Looks like Adafruit has breakout boards for that chip too.

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.

peepsalot posted:

The chinese arduino clones often use a CH34x serial converter chip, as opposed to the FTDI chip use in authentic ones. You likely need to install a driver for that serial converter if its not flashing right, but as sagebursh says the part of the message you posted so far is normal.

If I could only give one piece of advice to someone just starting out with Arduinos for the first time it's to just pony up the extra $5-$10 to get an authentic version rather than a knock-off. The authentic versions are well documented and pretty much every known potential pitfall has been discussed in detail on the internet somewhere. The knock offs introduce a ton of subtle issues (like serial -> usb interface chips) that can cause infinite frustration to new people who don't have the experience to differentiate between normal and unexpected behaviors.

Also that boat thing is amazing, nice job even if it was just helping out with the code! :)

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
Are analog RC filters on the switches an option?

Adbot
ADBOT LOVES YOU

PDP-1
Oct 12, 2004

It's a beautiful day in the neighborhood.
You can do a similar hysteresis function in hardware by having a byte for each pin that gets incremented (up to some max value) each time the button is pushed and decremented (limited to zero) when it isn't pushed. Only change the output state when the counter hits one of those limits.

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