Posts with «software hacks» label

Making an Arduino Ventilator? Read This First

Thanks to the virus crisis, lots of people are designing makeshift ventilator designs in the hopes of saving people’s lives. Many of these are based around some sort of Arduino-powered CPU. [Armstrong Subero] things that’s a great idea, but cautions that making an electronic pair of dice is a different proposition than creating a machine to breathe for someone. But he isn’t just complaining. He talks about considerations when building a real-time and safety-critical system.

[Armstrong] has a lot of good points, although we aren’t sure you need the complexity of a real-time operating system just to squeeze a bag. If anything, that seems like it might make it more susceptible to unexpected operation. However, we agree with his comments that you should have closed-loop control to make sure the device is working, alarming when the device isn’t working, and watchdog timers to guard against lockup.

One excellent point from the post:

For example a high availability system real time system may be specified as having an up time of around 99% in a 24 hour period. Which 1% of the day is it acceptable to have the ventilator not operational? Since we have 1440 minutes in a day, which 14.4 minutes of the day should the patient not be allowed to breathe?

However, he does have some solid suggestions such as using an IDE with debugging and adhering to a coding standard such as MISRA. Of course, he also points out you might choose a different CPU that has safety-critical certifications and corresponding libraries. One suggestion is to have multiple CPUs, and this is a common enough solution in many safety-critical systems. For example, imagine 3 CPUs driving a switching circuit that requires a low logic level to turn on.

You could make the outputs go to inputs if the CPU wants to not drive the switch, or pull the output to ground if it does. Then a pull-up resistor holds the state high if no CPU pulls it to ground. All CPUs could sense the state of the line and if they don’t think it looks right they sound their own alarm. Some systems vote so that two of three CPUs must agree (at least) or, in some cases, three out of five.

We’ve been talking about ventilators quite a bit lately. The kind of mechanical design [Armstrong] is probably thinking of is like the MIT design we talked about last week.

Counter-Strike Gets the RGB LED Treatment

Inspired by the over-the-top stage lighting and pyrotechnics used during e-sport events, [Hans Peter] set out to develop a scaled-down version (minus the flames) for his personal Counter-Strike: Global Offensive sessions. It might seem like pulling something like this off would involve hacking the game engine, but as it turns out, Valve was kind enough to implement a game state API that made it relatively easy.

According to the documentation, the CS:GO client can be configured to send out state information to a HTTP server at regular intervals. It even provided example code for implementing a simple state server in Node.js, which [Hans] adapted for this project by adding some conditional statements that analyze the status of the current game.

These functions fire off serial commands to the attached Arduino, which in turn controls the WS2812B LEDs. The Arduino code takes the information provided by the HTTP server and breaks that down into various lighting routines for different conditions such as wins and losses. But things really kick into gear when a bomb is active.

[Hans] wanted to synchronize the flashing LEDs with the beeping sound the bomb makes in the game, but the API doesn’t provide granular enough data. So he recorded the audio of the bomb arming sequence, used Audacity to precisely time the beeps, and implemented the sequence in his Arduino code. In the video after the break you can see that the synchronization isn’t perfect, but it’s certainly close enough to get the point across in the heat of battle.

With the special place that Counter-Strike occupies in the hearts of hackers and gamers alike, it’s little surprise people are still finding unique ways to experience the game.

Machine Learning on Tiny Platforms Like Raspberry Pi and Arduino

Machine learning is starting to come online in all kinds of arenas lately, and the trend is likely to continue for the forseeable future. What was once only available for operators of supercomputers has found use among anyone with a reasonably powerful desktop computer. The downsizing isn’t stopping there, though, as Microsoft is pushing development of machine learning for embedded systems now.

The Embedded Learning Library (ELL) is a set of tools for allowing Arduinos, Raspberry Pis, and the like to take advantage of machine learning algorithms despite their small size and reduced capability. Microsoft intended this library to be useful for anyone, and has examples available for things like computer vision, audio keyword recognition, and a small handful of other implementations. The library should be expandable to any application where machine learning would be beneficial for a small embedded system, though, so it’s not limited to these example applications.

There is one small speed bump to running a machine learning algorithm on your Raspberry Pi, though. The high processor load tends to cause small SoCs to overheat. But adding a heatsink and fan is something we’ve certainly seen before. Don’t let your lack of a supercomputer keep you from exploring machine learning if you see a benefit to it, and if you need more power than just one Raspberry Pi you can always build a cluster to get your task done just a little bit faster, too.

Thanks to [Baldpower] for the tip!

Push it to the Limit: SSD1306 at 150 FPS

A good deal of the projects we cover here at Hackaday are not, in the strictest sense, practical endeavors. If we required that everything which graced our digital pages had a clear end result, the site would be in a rather sad state of affairs. Sometimes it’s enough just to do something for the challenge of it. But more often than not, you’ll learn something in the process which you can use down the line.

That’s precisely what pushed [Laurence Bank] to see how well he could optimize the frame rate on the popular SSD1306 OLED display. After several iterations of his code, he was able to achieve a blistering 151.5 FPS, with apparently still some room for improvement if he’s feeling up to the challenge. But considering his first attempt was only running at 5.5 FPS, we’d say he’s already more than earned his hacker cred on this one.

A few different tricks were used to achieve such incredible performance gains. To start with, while the official I2C specification says you’re supposed to wait for an acknowledgment back from the device when communicating with it, [Laurence] realized the SSD1306 didn’t actually care. He could continuously blast commands at the display without bothering to wait for an acknowledgment. He admits there are problems with this method, but you can’t argue with the results.

To really wring all the performance out of the system he could, [Laurence] donned his Assembly Cap and examined how the Arduino IDE compiler was interpreting his code. He identified a few areas where changing his C code would force the compiler to generate faster output. He notes that this wouldn’t normally be required when working with more advanced compilers, but that the Arduino toolchain needs its hand held occasionally.

This isn’t the first time we’ve seen somebody try and push more pixels through the very same OLED display, and it’s interesting to see the two very different approaches to the same goal.

Real-Time Planet Tracker With Laser-Point Accuracy

Space. The final frontier. Unfortunately, the vast majority of us are planet-locked until further notice. If you are dedicated hobbyist astronomer, you probably already have the rough positions of the planets memorized. But what if you want to know them exactly from the comfort of your room and educate yourself at the same time? [Shubham Paul] has gone the extra parsec to build a Real-Time Planet Tracker that calculates their locations using Kepler’s Laws with exacting precision.

An Arduino Mega provides the brains, while 3.5-turn-pan and 180-degree-tilt servos are the brawn. A potentiometer and switch allow for for planet and mode selection, while a GPS module and an optional MPU9250 gyroscope/magnetometer let it know where you are. Finally a laser pointer shows the planet’s location in a closed room. And then there’s code: a lot of code.

The hardware side of things — as [Shubham Paul] clarifies — looks a little unfinished because the focus of the project is the software with the intent to instruct. They have included all the code they wrote for the RTPT, providing a breakdown in each section for those who are looking to build their own.

There is an extra step to auto-align the RTPT to north, otherwise you’ll have to do so manually. But [Shubham Paul] has designed it so that even if you move the tracker about, the RTPT will readjust its calculations in real time. Each part of the project includes a wealth of related information beyond simple instructions to adequately equip any prospective builders.

This hack gets the job done. If it’s looks you’re after, an artistic expression of maker skills and astronomy can be seen in this planetary map that relies on persistence of vision.


Filed under: Arduino Hacks, software hacks

Code Craft-Embedding C++: Hacking the Arduino Software Environment

The Arduino software environment, including the IDE, libraries, and general approach, are geared toward education. It’s meant as a way to introduce embedded development to newbies. This is a great concept but it falls short when more serious development or more advanced education is required. I keep wrestling with how to address this. One way is by using Eclipse with the Arduino Plug-in. That provides a professional development environment, at least.

The code base for the Arduino is another frustration. Bluntly, the use of setup() and loop() with main() being hidden really bugs me. The mixture of C and C++ in libraries and examples is another irritation. There is enough C++ being used that it makes sense it should be the standard. Plus a good portion of the library code could be a lot better. At this point fixing this would be a monumental task requiring many dedicated developers to do the rewrite. But there are a some things that can be done so let’s see a couple possibilities and how they would be used.

The Main Hack

As mentioned, hiding main() bugs me. It’s an inherent part of C++ which makes it an important to learning the language. Up until now I’d not considered how to address this. I knew that an Arduino main() existed from poking around in the code base – it had to be there because it is required by the C++ standard. The light dawned on me to try copying the code in the file main.cpp into my own code. It built, but how could I be sure that it was using my code and not the original from the Arduino libraries? I commented out setup() and it still built, so it had to be using my version otherwise there’d be an error about setup() being missing. You may wonder why it used my version.

When you build a program… Yes, it’s a “program” not a “sketch”, a “daughter board” not a “shield”, and a “linker” not a “combiner”! Why is everyone trying to change the language used for software development?

When you build a C++ program there are two main stages. You compile the code using the compiler. That generates a number of object files — one for each source file. The linker then combines the compiled objects to create an executable. The linker starts by looking for the C run time code (CRTC). This is the code that does some setup prior to main() being called. In the CRTC there will be external symbols, main() being one, whose code exists in other files.

The linker is going to look in two places for those missing symbols. First, it loads all the object files, sorts out the symbols from them, and builds a list of what is missing. Second, it looks through any included libraries of pre-compiled objects for the remaining symbols. If any symbols are still missing, it emits an error message.

If you look in the Arduino files you’ll find a main.cpp file that contains a main() function. That ends up in the library. When the linker starts, my version of main() is in a newly created object file. Since object files are processed first the linker uses my version of main(). The library version is ignored.

There is still something unusual about main(). Here’s the infinite for loop in main():

	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}

The call to loop() is as expected but why is there an if statement and serialEventRun? The function checks if serial input data is available. The if relies on a trick of the tool chain, not C++, which checks the existence of the symbol serialEventRun. When the symbol does not exist the if and its code are omitted.

Zapping setup() and loop()

Now that I have control over main() I can address my other pet peeve, the setup() and loop() functions. I can eliminate these two function by creating my own version of main(). I’m not saying the use of setup() and loop() were wrong, especially in light of the educational goal of Arduino. Using them makes it clear how to organize an embedded system. This is the same concept behind C++ constructors and member functions. Get the initialization done at the right time and place and a good chunk of software problems evaporate. But since C++ offers this automatically with classes, the next step is to utilize C++’s capabilities.

Global Instantiation

One issue with C++ is the cost of initialization of global, or file, scope class instances. There is some additional code executed before main() to handle this as we saw in the article that introduced classes. I think this overhead is small enough that it’s not a problem.

An issue that may be a problem is the order of initialization. The order is defined within a compilation unit (usually a file) from the first declaration to the last. But across compilation units the ordering is undefined. One time all the globals in file A may be initialized first and the next time those in file B might come first. The order is important when one class depends on another being initialized first. If they are in different compilation units this is impossible to ensure. One solution is to put all the globals in a single compilation unit. This may not work if a library contains global instances.

A related issue occurs on large embedded computer systems, such as a Raspberry Pi running Linux, when arguments from the command line are passed to main(). Environment variables are also a problem since they may not be available until main() executes. Global instance won’t have access to this information so cannot use it during their initialization. I ran into this problem with my robots whose control computer was a PC. I was using the robot’s network name to determine their initial behaviors. It wasn’t available until main() was entered, so it couldn’t be used to initialize global instances.

This is an issue with smaller embedded systems that don’t pass arguments or have environment values but I don’t want to focus only on them. I’m looking to address the general situation that would include larger systems so we’ll assume we don’t want global instances.

Program Class

The approach I’m taking and sharing with you is an experiment. I have done something similar in the past with a robotics project but the approach was not thoroughly analyzed. As often happens, I ran out of time so I implemented this as a quick solution. Whether this is useful in the long run we’ll have to see. If nothing else it will show you more about working with C++.

My approach is to create a Program class with a member run() function. The setup for the entire program occurs in the class constructor and the run() function handles all the processing. What would normally be global variables are data members.

Here is the declaration of a skeleton Program class and the implementation of run():

class Program {
public:
	void run();
	static Program& makeProgram() {
		static Program p;
		return p;
	}

private:
	Program() { }
	void checkSerialInput();
};

void Program::run() {
	for (;;) {
		// program code here
		checkSerialInput();
	}
}

We only want one instance of Program to exist so I’ve assured this by making the constructor private and providing the static makeProgram() function to return the static instance created the first time makeProgram() is called. The Program member function checkSerialInput() handles checking for the serial input as discussed above. In checkSerialInput() I introduced an #if block to eliminate the actual code if the program is not using serial input.

Here is how Program is used in main.cpp:


void arduino_init() {
	init();
	initVariant();
}

int main(void) {
	arduino_init();
	Program& p = Program::makeProgram();
	p.run();
	return 0;
}

The function initArduino() is inlined and handles the two initialization routines required to setup the Arduino environment.

One of the techniques for good software development is to hide complexity and provide a descriptive name for what it does. These functions hide not only the code but, in one case, the conditional compilation.

Redbot Line Follower Project

This code experiment uses a Sparkfun Redbot setup for line following. This is a two wheeled robot with 3 optical sensors to detect the line and an I2C accelerometer to sense bumping into objects. The computer is a Sparkfun Redbot Mainboard which is compatible with the Arduino Uno but provides a much different layout and includes a motor driver IC.

This robot is simple enough to make a manageable project but sufficiently complex to serve as a good test, especially when the project gets to the control system software. The basic code for handling these motors and sensors comes from Sparkfun and uses only the basic pin-level Arduino routines. I can’t possibly hack the entire Arduino code but using the Sparkfun code provides a manageable subset for experimenting.

For this article we’ll just look at the controlling the motors. Let’s start with the declaration of the Program class for testing the motor routines:

class Program {
public:
	void run();
	static Program& makeProgram() {
		static Program p;
		return p;
	}

private:
	Program() { }
	static constexpr int delay_time { 2000 };

	rm::Motor l_motor { l_motor_forward, l_motor_reverse, l_motor_pwm };
	rm::Motor r_motor { r_motor_forward, r_motor_reverse, r_motor_pwm };
	rm::Wheels wheels { l_motor, r_motor };

	void checkSerialInput();
};

There is a namespace rm enclosing the classes I’ve defined for the project, hence the rm:: prefacing the class names. On line 11 is something you may not have seen, a constexpr which is new in C++ 11 and expanded in C++14. It declares that delay_time is a true constant used during compilation and will not be allocated storage at run-time. There is a lot more to constexpr and we’ll see it more in the future. One other place I used it for this project is to define what pins to use. Here’s a sample:

constexpr int l_motor_forward = 2;
constexpr int l_motor_reverse = 4;
constexpr int l_motor_pwm = 5;
constexpr int r_motor_pwm = 6;
constexpr int r_motor_forward = 7;
constexpr int r_motor_reverse = 8;

The Motor class controls a motor. It requires two pins to control the direction and one pulse width modulation (PWM) pin to control the speed. The pins are passed via constructor and the names should be self-explanatory. The Wheels class provides coordinated movement of the robot using the Motor instances. The Motor instances are passed as references for the use of Wheels. Here are the two class declarations:

class Motor : public Device {
public:
	Motor(const int forward, const int reverse, const int pwm);

	void coast();
	void drive(const int speed);

	int speed() const {
		return mSpeed;
	}

private:
	void speed(const int speed);

	PinOut mForward;
	PinOut mReverse;
	PinOut mPwm;
	int mSpeed { };
};


class Wheels {
public:
	Wheels(Motor& left, Motor& right) :
			mLeft(left), mRight(right) {
	}

	void move(const int speed) {
		drive(speed, speed);
	}
	void pivot(const int speed) {
		drive(speed, -speed);
	}
	void stop() {
		mLeft.coast();
		mRight.coast();
	}

	void drive(const int left, const int right) {
		mLeft.drive(left);
		mRight.drive(right);
	}

private:
	Motor& mLeft;
	Motor& mRight;
};

The workhorse of Wheels is the function drive() which just calls the Motor drive() functions for each motor. Except for stop(), the other Wheels functions are utilities that use drive() and just make things easier for the developer. The compiler should convert those to a direct call to driver() since they are inline by being inside the class declaration. This is one of the interesting ways of using inline functions to enhance the utility of a class without incurring any cost in code or time.

The run() method in Program tests the motors by pivot()ing first in one direction and then the other at different speeds. A pivot() rotates the robot in place. Once the speed is set it continues until changed so the delay functions simply provide a little time for the robot to turn. Here’s the code:

void Program::run() {
	for (;;) {
		wheels.pivot(50);
		delay (delay_time);

		wheels.pivot(-100);
		delay(delay_time);

		checkSerialInput();
		if (serialEventRun) {
		}
	}
}

Wrap Up

The Redbot project is an interesting vehicle for demonstrating code techniques. The current test of the motor routines demonstrates how to override the existing Arduino main(). Even if you don’t like my approach with Program, the flexibility of using your own main() may come in handy for your own projects. The next article is going to revisit this program using templates.

THE EMBEDDING C++ PROJECT

Over at Hackaday.io, I’ve created an Embedding C++ project. The project will maintain a list of these articles in the project description as a form of Table of Contents. Each article will have a project log entry for additional discussion. Those interested can delve deeper into the topics, raise questions, and share additional findings.

The project also will serve as a place for supplementary material from myself or collaborators. For instance, someone might want to take the code and report the results for other Arduino boards or even other embedded systems. Stop by and see what’s happening.


Filed under: Arduino Hacks, Hackaday Columns, Software Development, software hacks

Code Craft: When #define is Considered Harmful

An icon of Computer Science, [Edsger Dijkstra], published a letter in the Communications of the Association of Computer Machinery (ACM) which the editor gave the title “Go To Statement Considered Harmful“. A rousing debate ensued. A similar criticism of macros, i.e. #define, in C/C++ may not rise to that level but they have their own problems.

Macros are part of the preprocessor for the C/C++ languages which manipulates the source code before the actual translation to machine code. But there are risks when macros generate source code. [Bjarne Stroustrup] in creating C++ worked to reduce the need and usage of the preprocessor, especially the use of macros. In his book, The C++ Programming Language he writes,

Don’t use them if you don’t have to. Almost every macro demonstrates a flaw in the programming language, in the program, or in the programmer.

As C retrofitted capabilities of C++, it also reduced the need for macros, thus improving that language.

With the Arduino using the GNU GCC compilers for C and C++ I want to show new coders a couple of places where the preprocessor can cause trouble for the unwary. I’ll demonstrate how to use language features to achieve the same results more cleanly and safely. Of course, all of this applies equally when you use any of these languages on other systems.

We’re only going to be looking at macros in this article but if you want to read more the details about them or the preprocessor see the GNU GCC Manual section on the preprocessor.

Basic Macro Usage

The preprocessor is complex, but described in simplified terms, it reads each line in a compilation unit, i.e. file, scanning for lines where the first non-whitespace character is a hash character (#). There may be whitespace before and after the #. The next token, i.e. a set of characters bounded by whitespace, is the name of the macro. Everything following the name is the argument. A macro has the form:

#define <name> <rest of line>

The simplest macro usage is to create symbols that are used to control the preprocessor or as text substitution in lines of code. A symbol can be created with or without a value. For example:

#define LINUX 
#define VERSION 23 

The first line defines the symbol LINUX but does not give it a value. The second line defines VERSION with the value 23. This is how constant values were defined pre-C++ and before the enhancements to C.

By convention, macro symbol names use all caps to distinguish them from variable and function names.

Symbols without values can only be used to control the preprocessor. With no value they would simply be a blank in a line of code. They are used in the various forms of the #if preprocessor directives to determine when lines of code are included or excluded.

When a symbol with a value appears in a line of code, the value is substituted in its place. Here is how using a macro with a value looks:

const int version_no = VERSION; 

which results in the code

const int version_no = 23; 

This type of macro usage doesn’t pose much of a threat that problems will arise. That said, there is little need to use macros to define constants. The language now provides the ability to declare named constants. One reason macros were used previously was to avoid allocating storage for a value that never changes. C++ changed this and constant declarations do not allocate storage. I’ve tested this on the Arduino IDE, and found that C does not appear to allocate storage but I’ve seen mention that C may do this on other systems.

Here is the current way to define constants:

const int version = 23;
enum {start=10, end=12, finish=24};   // an alternative for related integer consts

Function Macros

Another form of macro is the function macro which, when invoked looks like a function call, but it is not. Similar to the symbol macros, function macros were used to avoid the overhead of function calls for simple sequences of code. Another usage was to provide genericity, i.e. code that would work for all data types.

Function macros are used to pass parameters into the text replacement process. This is fraught with danger unless you pay close attention to the details. The use of inline functions is much safer as I’ll show below.

To illustrate here’s an example of a function macro to multiply two values.

#define MULT(lhs, rhs) lhs * rhs

This function macro is used in source code as:

int v_int = MULT(23, 25);
float v_float = MULT(23.2, 23.3);

Consider this use of the macro, its expansion, and its evaluation, which definitely does not produce the expected result:

int s = MULT(a+b, c+d);
// translates to: int s = a + b * c + d;
// evaluates as: a + (b * c) + d

This can be addressed by adding parenthesis to force the proper evaluation order of the resulting code. Adding the parenthesis results in this code:

#define MULT(lhs, rhs) ((lhs) * (rhs))
int s = MULT(a+b, c+d);
// now evaluates as: (a + b) * (c + d)

The parenthesis around lhs force (a + b) to be evaluated before the multiplication is performed.

Another ugly case is:

#define POWER(value) ((value) * (value))
int s = POWER(a++);
// evaluates as: ((a++) * (a++))

Now there are two problems. First, a is incremented twice, and, second, the wrongly incremented version is used for the calculation. Here again it does not produce the desired result.

It’s really easy to make a mistake like this with function macro definitions. You’re better off using an inline function which is not prone to these errors. The inline equivalents are:

inline int mult(const int x, const int y) { return (x * y); }
inline int power(const int x) { return (x * x); }
 

Now the values of x and y are evaluated before the function is called. The increment or arithmetic operators are no longer evaluated inside the actual function. Remember, an inline function does not produce a function call since it is inserted directly into the surrounding code.

In C, there is a loss of generality using inline over the macro. The inline functions shown only support integers. You can add similar functions for different data types, which the standard libraries do, but the names must reflect the data type. A few cases would be covered by mult_i, mult_f,  mult_l, and mult_d for integer, float, long and double, respectively.

This is less of a problem in C++ where there are two solutions. One is to implement separate functions, as in C, but the function names can all be mult relying on C++’s ability to overload function names.

A nicer C++ version is to use template functions. These really are straightforward for simple situations. Consider:

template <typename T>
inline T mult(const T x, const T y) { return (x * y); }
template <typename T>
inline T power(const T x) { return (x * x); }

You use these just like any other function call and the compiler figures out what to do. There is still one minor drawback. The mult cannot mix data types which MULT has no problem doing. You must use an explicit cast to make the types agree.

The code generated by the inline and template versions are going to be the same as the macro version, except they will be correct. You should restrict the use of macros to preprocessing of code,  not code generation. It’s safer and once you are used to the techniques it’s easy.

If these problems aren’t enough, take a look at the GNU preprocessor manual section which provides more details and examples of problems.

Stringification and Concatenation

The previous sections discussed the problems with macros and how to avoid them using C/C++ language constructs. There are a couple of valuable uses of macros that we’ll discuss in this section.

The first is stringification which converts a function macro argument into a C/C++ string. The second is concatenation which combines two arguments into a single string.

A string is created when a # appears before a token. The result is a string: #quit becomes “quit”.

Two arguments are concatenated when ## appears between them: quit ## _command becomes quit_command.

This is useful in building tables of data to use in a program. An illustration:

#define COMMAND(NAME) { #NAME, NAME ## _command }

struct command commands[] =
{
COMMAND (quit),
COMMAND (help),
...
};

expands to the code

struct command
{
char *name;
void (*function) (void);
};

struct command commands[] =
{
{ "quit", quit_command },
{ "help", help_command },
...
};

Wrapup

The C/C++ preprocessor is powerful and dangerous. The standards committees have followed Stroustrup’s lead in adding features that reduce the need to use the preprocessor. There is still a need for it and probably always will be since it is an inherent part of the languages. Be careful when and how you use #define, and use it sparingly.


Filed under: Hackaday Columns, Software Development, software hacks

HC-SR04 Isn’t the Same as Parallax PING))) But It Can Pretend to Be!

“It’s only software!” A sentence that strikes terror in the heart of an embedded systems software developer. That sentence is often uttered when the software person finds a bug in the hardware and others assume it’s going to be easier for fix in software rather than spin a new hardware revision. No wonder software is always late.

[Clint Stevenson] is his own hardware and software guy, as are most of us. He wanted to use the less expensive HC-SR04 ultrasonic rangefinder in a prototype. Longer term he wanted to have the choice of either a Parallax PING or MaxBotix ultrasonic sensor for their better performance outdoors. His hardware hack of the SR04 made this a software problem which he also managed to solve!

[Clint] was working with the Arduino library, based on the Parallax PING, which uses a single pin for trigger and echo. The HC-SR04 uses separate pins. Originally he modified the Arduino library to accept the two pin approach. But with his long term goal in mind, he also modified the HC-SR04 sensor by removing the on-board pull-up resistor and adding a new one on the connector side to combine the signals. That gave him an SR04 that worked with the single-pin based library.

We’ve seen Parallax PING projects for sensing water depth and to generate music. These could be hacked to use the HC-SR04 using [Clint’s] techniques.

[Arduino and HC-SR04 photo from http://www.blaxlab.com/%5D


Filed under: Arduino Hacks, Holiday Hacks, software hacks