Sunday, April 21, 2019

San Juan Islands, WA

Data logging in San Juan Islands, Washington:

Apr 19-21, San Juan Island

Apr 22, San Juan Island to Orcas Island

Apr 23, Hiking in Orcas Island

Apr 24, Hiking in Orcas Island, Mountain Lake

Apr-25, Back ashore


Data logging was done as explained here, here and here.

Tuesday, March 12, 2019

Easy Low Pass Filter

Instead of implementing a buffer and smooth it, there is a much easier and efficient way to do it. Here is a simple JavaScript implementation. First you define your accumulator function:
 function lowPass(alpha, value, acc) {
   return (value * alpha) + (acc * (1 - alpha));
 }
Then you need to define your APLHA coefficient:
 const ALPHA = 0.015;
Then you can invoke the accumulator with the aplha coefficient on the data to smooth:
 let filteredGustArray = [];
 let acc = 0;
 data.data.forEach(dp => {
   acc = lowPass(ALPHA, dp.gust, acc);
   filteredGustArray.push(acc);
 });
This produces an array containing the smoothed data. Here is a representation of what it looks like, along with the data to smooth (raw data in red, smoothed data in blue):
The demo data are available here. Just run the script with nodejs like
 $ node max.gust.js both > data.csv
This will produce a csv file you can then import into any spreadsheet program, to see the figure above.

Monday, March 11, 2019

Smart TCP Watch, prototype.

TCP, no BlueTooth (and as a result, no Smart Phone) is required. The "watch" can connect directly to the network.
See a first prototype here.
And a short video here.

Monday, November 26, 2018

Saturday, August 25, 2018

Smart watch..., who's smart, who's watching?

I got a Pebble watch a while back, for $100 it did everything I was expecting from it. I came with a cloud/web-based IDE, a really fine piece of work, it was possible to develop and debug applications running on the watch in JavaScript, that was really well done, smart and neat.
Then a while back, Pebble got acquired by Fitbit. And now, the Cloud IDE of Pebble is not available anymore. Than means I cannot develop new apps for my Pebble, even if it is still working just fine.
Why would I buy a new watch (twice as expensive), and re-write all my apps? This is quite frustrating...
In fact, there was something wrong from the beginning.
All those so-called smart watches need a Bluetooth cell-phone to connect to, and from there it will reach other data. Why not a TCP-based protocol from the watch, to bypass the phone? Power consumption? I doubt it.
This is not about accessibility or configuration either, I do it all the time for Raspberry Pis and similar boards, ssh and similar protocols have been here for this kind of remote access, for ages, and for good reasons.
Smart glasses, head-up displays (HUD), smart watches, all those devices are just displays, all they need is to connect to a data bus and display what they mean to (just like an NMEA bus).
I suspect some marketing bullshit behind the scene..., again.

If this kind of TCP watch does not show up soon, I'll build one. Bam!

Saturday, July 14, 2018

Raspberry PI, PWM, servos, and PCA9685

The code mentioned below can be found in this git repo.
Pulse Width Modulation (PWM) is the technique used from a digital source to simulate an analog output.
For example, imagine that you want to dim an led from a digital device, to make it look like it is glowing. The digital device only has pins that can take 2 values: 0 or 3V3.
0 means that the led will be off, 3V3 means it will be on, at 100% of its brightness.
In short, it is on or off, and there is nothing in between.
But here is an idea to work around that issue:
To show it at 50% of its brightness, the idea is to turn it off 50% of the time, and on 50% of the time.
To show it at 25% of its brightness, it will be on 25% of the time, and off 75% of the time.
If the on-off cycles are short and fast enough, a human eye will no be able to see them, it will only have the illusion of the resulting brightness.
A human eye cannot make the distinction between images separated by less than one 10th of a second. That is why the movies are shot at 24 images per second, so you cannot tell the difference between the frames.
This technique is call Persistence of Vision (POV).
The #1 parameter of PoV is the human retina. To have an idea of how much it is important, just put your cat in front of a TV, and see how much he/she reacts. To a cat, it might just be a fuzzy screen...
The early movies - like Charlie Chaplin's silent ones - were shot at 16 images per second, fast enough to induce POV. They were later projected by faster projectors - 24 frames per second. That is why the characters seem to move faster. They were originally moving normally.


Here are examples of PWM applied to POV:
At work, for real:
The PCA9685 is a servo driver PCB.
The Raspberry PI does not have analog pins, we need to use Pulse Width Modulation to simulate analog values, a servo is an analog device.
We use for that the method setPWM(channel, 0, pulse), that will eventually write to the registers of the device.
An instruction like setPWM(channel, 0, pulse) means:
  • On channel channel (0 to 15 on the PCA9685)
  • in each cycle, turn the power on between 0 and pulse.
pulse has a value between 0 and 4095, that is 4096 distinct values, 4096 is 212, the PCA9685 is a 12 bit device.

The frequency

The frequency is provided in Hertz (Hz). A frequency of 60 means 60 cycles per second.
At 60 Hz, a cycle will be 1 / 60 second, which is 0.01666666 second, or 16.66666 milli-second (ms).

The pulse

For each of the cycles set above by setting the frequency, we need to determine the int value, between 0 and 4095, corresponding to the pulse in milliseconds we want to simulate with PWM.
In the class i2c.servo.pwm.PCA9685.java, this is done in this method:
public static int getServoValueFromPulse(int freq, float targetPulse) {
  double pulseLength = 1_000_000; // 1s = 1,000,000 us per pulse. "us" is to be read "micro (mu) sec".
  pulseLength /= freq;  // 40..1000 Hz
  pulseLength /= 4_096; // 12 bits of resolution. 4096 = 2^12
  int pulse = (int) Math.round((targetPulse * 1_000) / pulseLength); // in millisec
  if (verbose) {
    System.out.println(String.format("%.04f \u00b5s per bit, pulse: %d", pulseLength, pulse));
  }
  return pulse;
}
The cycle length (in ms) obviously depends on the frequency.
The pulse required for the servo to work is emitted once per cycle.

Example

As an example, let us calculate for a 60 Hz frequency the pulse value to send to setPWM(channel, 0, pulse) for a 1.5 millisecond PWM:
  • 1 cycle has a duration of 1 / 60 second, or 16.66666 milliseconds.
  • each cycle is divided in 4096 slots, we can say that 4096 bits = 16.6666 ms.
  • the solution is provided by a rule of three: value = 4096 * (pulse / 16.66666), which is 368.64, rounded to 369.

A comment about servos' compliance and reliability

Theoretically, servos follow those rules:
PulseStandardContinuous
1.5 ms0 °Stop
2.0 ms90 °FullSpeed forward
1.0 ms-90 °FullSpeed backward
That happens not to be always true, some servos (like https://www.adafruit.com/product/169 or https://www.adafruit.com/product/155) have values going between 0.5 ms and 2.5 ms.
Before using them, servos should be calibrated. You can use the class i2c.samples.IntercativeServo.java can be used for that, you set the pulse values interactively, and you see what the servo is doing.
$> ./inter.servo
Connected to bus. OK.
Connected to device. OK.
freq (40-1000)  ? > 60
Setting PWM frequency to 60 Hz
Estimated pre-scale: 100.72526
Final pre-scale: 101.0
Servo Channel (0-15) : 1
Entry method: T for Ticks (0..4095), P for Pulse (in ms) > p
Enter 'quit' to exit.
Pulse in ms > 1.5
setServoPulse(1, 1.5)
4.0690 μs per bit, pulse:369
-------------------
Pulse in ms > 0.5
setServoPulse(1, 0.5)
4.0690 μs per bit, pulse:122
-------------------
Pulse in ms > 0.6
setServoPulse(1, 0.6)
4.0690 μs per bit, pulse:147
-------------------
Pulse in ms > 2.4
setServoPulse(1, 2.4)
4.0690 μs per bit, pulse:589
-------------------
Pulse in ms > 2.5
setServoPulse(1, 2.5)
4.0690 μs per bit, pulse:614
-------------------
... etc.

Once you have determined the appropriate min and max values, you also have the int values to feed the setPWM with.

Some links: