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.
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.csvThis will produce a
csv file you can then import into any spreadsheet program, to see the figure above.
ssh and similar protocols have been here for this kind of remote access, for ages, and for good reasons.
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.
50% of its brightness, the idea is to turn it off 50% of the time, and on 50% of the time.
25% of its brightness, it will be on 25% of the time, and off 75% of the time.
setPWM(channel, 0, pulse), that will eventually write to the registers
of the device.
setPWM(channel, 0, pulse) means:
channel (0 to 15 on the PCA9685)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.
1 / 60 second, which is 0.01666666 second, or 16.66666 milli-second (ms).
int value,
between 0 and 4095, corresponding to the pulse in milliseconds we want to simulate with PWM.
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.
60 Hz frequency the pulse value to send to setPWM(channel, 0, pulse)
for a 1.5 millisecond PWM:
1 / 60 second, or 16.66666 milliseconds.4096 slots, we can say that 4096 bits = 16.6666 ms.value = 4096 * (pulse / 16.66666), which is 368.64, rounded to 369.| Pulse | Standard | Continuous |
|---|---|---|
| 1.5 ms | 0 ° | Stop |
| 2.0 ms | 90 ° | FullSpeed forward |
| 1.0 ms | -90 ° | FullSpeed backward |
0.5 ms and 2.5 ms.
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.
For the fun: Same problem addressed in several languages, read the paper here.
It is about matrix and systems of equations resolution, curve smoothing, etc.
Done in C, Java, Processing, Scala, Kotlin, Python, JavaScript, Groovy, Go, Clojure (in progress), ...
JavaMail is a Java package that has been available for ever, it understands the email protocols (IMAP, POP3, SMTP, etc), and can be used to interact with email accounts programmatically.
$ git clone https://github.com/OlivierLD/raspberry-coffee.git $ cd raspberry-coffee $ cd common-utils $ ../gradlew shadowJar $ cp email.properties.sample email.properties $ vi email.properties $ # Here you modify your properties file to match your email account $ java -cp ./build/libs/common-utils-1.0-all.jar email.examples.EmailWatcher -send:google -receive:googleThe
-send:google -receive:google depends on the settings in your email.properties.
email.properties, send a message like this:
Subject: execute Content: whoami ifconfig uname -aNote: this example requires the content to be in
plain/text.
EmailWatcher, it sends you an acknowledgement:
Then, the 3 commands are processed by the EmailWatcher, you would see in its console an output like that:
Start receiving. Received: whoami ifconfig uname -a Operation: [execute], sent for processing... pi lo0: flags=8049And finally, you receive an email like that: ... meaning that the commands you've sent have been executed.mtu 16384 options=1203 inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 ...
You can also attach the script to execute to a blank email, with topic execute-script:
Attach a file like this:
#!/bin/bash whoami ifconfig ps -ef | grep EmailWatcher... and just wait for the result to come back to you:
Scripts execution returned: pi eth0: flags=4099mtu 1500 ether a4:ba:db:c9:04:2e txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device interrupt 18 lo: flags=73 mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10 loop txqueuelen 1 (Local Loopback) RX packets 9215 bytes 2022884 (1.9 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 9215 bytes 2022884 (1.9 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 wlan0: flags=4163 mtu 1500 inet 192.168.42.3 netmask 255.255.255.0 broadcast 192.168.42.255 inet6 fe80::4038:1f53:b94f:ccc2 prefixlen 64 scopeid 0x20 ether 78:e4:00:78:ad:8f txqueuelen 1000 (Ethernet) RX packets 8848724 bytes 696021134 (663.7 MiB) RX errors 0 dropped 0 overruns 0 frame 18848059 TX packets 6040965 bytes 795472510 (758.6 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device interrupt 17 base 0xc000 pi 12476 12472 1 16:39 pts/0 00:00:53 java -cp ./build/libs/RasPISamples-1.0-all.jar weatherstation.email.EmailWatcher -send:google -receive:google pi 16204 16199 0 18:04 pts/0 00:00:00 grep EmailWatcher >> sh ./attachments/2018-04-26_18-04-27/sample.sh returned status 0
Having the command
java -cp ./build/libs/common-utils-1.0-all.jar email.examples.EmailWatcher -send:google -receive:googlefired when the machine boots will allow you make sure it is waiting for your emails as soon as the machine is up.
This EmailWatcher as it is also allows you to execute scripts, attached to the email. Look into the code for details ;)
It is even possible to ssh to another machine and execute a bunch of commands stored in a script... The command you send in the email's body would be like
ssh pi@192.148.42.13 bash -s < ~/nodepi.banner.shIf a password is required, use
sshpass:
sshpass -p 'secret-password' ssh pi@192.148.42.13 bash -s < ~/nodepi.sudo.shYou can even
sudo:
echo 'secret-password' | sudo -S privilegedCommandThis can be dangerous, hey? With great power come great responsibilities...
kiosk mode, running on a Raspberry PI with a touch screen attached to it.
.mirror {
display: block;
-webkit-transform: matrix(-1, 0, 0, 1, 0, 0);
-moz-transform: matrix(-1, 0, 0, 1, 0, 0);
-o-transform: matrix(-1, 0, 0, 1, 0, 0);
transform: matrix(-1, 0, 0, 1, 0, 0);
}
.upside-down {
height: 100%;
width: 100%;
-moz-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
-o-transform: rotate(180deg);
transform: rotate(180deg);
}
.mirror-upside-down {
display: block;
-webkit-transform: matrix(-1, 0, 0, 1, 0, 0) rotate(180deg);
-moz-transform: matrix(-1, 0, 0, 1, 0, 0) rotate(180deg);
-o-transform: matrix(-1, 0, 0, 1, 0, 0) rotate(180deg);
transform: matrix(-1, 0, 0, 1, 0, 0) rotate(180deg);
}
In the picture above, we use the class as follow:
<div id="the-div" class="mirror-upside-down big" style="padding: 0px; text-align: center;">
<hr/>
<table>
<tr>
<td colspan="2">GPS Data</td>
</tr>
<tr>
<td>
<span>Your position:</span>
<br/>
<span>N 37° 44.93'</span>
...
The page on the screen (not on the wind shield) would actually look like this:
| GPS Data | |
|
Your position:
N 37° 44.93' W 122°30.42' |
Your Speed:
12.34 kts |
.mirror-upside-down {
display: block;
-webkit-transform: matrix(-1, 0, 0, 1, 0, 0) rotate(180deg) perspective(50em) rotateX(-40deg);
-moz-transform: matrix(-1, 0, 0, 1, 0, 0) rotate(180deg) perspective(50em) rotateX(-40deg);
-o-transform: matrix(-1, 0, 0, 1, 0, 0) rotate(180deg) perspective(50em) rotateX(-40deg);
transform: matrix(-1, 0, 0, 1, 0, 0) rotate(180deg) perspective(50em) rotateX(-40deg);
}
| GPS Data | |
|
Your position:
N 37° 44.93' W 122°30.42' |
Your Speed:
12.34 kts |
Docker can work around the "But it works on my machine!.." syndrome.
nodejs project you want to share with others.
WebSocket server.
gitNodeJS and npmgit repositorynode modulesnode server with the right scriptDocker can take care of the steps 3 to 9.
It will build the image, and then run it.
Docker on the machine (the Raspberry PI here),
as explained here.
Dockerfile like this (available here):
FROM resin/raspberrypi3-debian:latest LABEL maintainer="Olivier LeDiouris <olivier@lediouris.net>" RUN echo "alias ll='ls -lisah'" >> $HOME/.bashrc RUN apt-get update RUN apt-get install sysvbanner RUN apt-get install -y curl git build-essential RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - RUN apt-get install -y nodejs RUN echo "banner Node-PI" >> $HOME/.bashrc RUN echo "git --version" >> $HOME/.bashrc RUN echo "echo -n 'node:' && node -v" >> $HOME/.bashrc RUN echo "echo -n 'npm:' && npm -v" >> $HOME/.bashrc RUN mkdir /workdir WORKDIR /workdir RUN git clone https://github.com/OlivierLD/node.pi.git WORKDIR /workdir/node.pi RUN npm install EXPOSE 9876 CMD ["npm", "start"]
Docker image creation (named oliv-nodepi below) comes down to 1 line (the one in bold red):
$ docker build -t oliv-nodepi . Sending build context to Docker daemon 752.6kB Step 1/20 : FROM resin/raspberrypi3-debian:latest ---> c542b8f7a388 Step 2/20 : MAINTAINER Olivier LeDiouris---> Using cache ---> b2ff0d7c489f Step 3/20 : ADD nodepi.banner.sh / ---> 535733298dd1 Step 4/20 : RUN echo "alias ll='ls -lisah'" >> $HOME/.bashrc ---> Running in 09baf7261a55 Removing intermediate container 09baf7261a55 ---> 71e1e4c95663 Step 5/20 : RUN apt-get update ---> Running in 5d817a941a14 Get:1 http://security.debian.org jessie/updates InRelease [94.4 kB] Get:2 http://archive.raspbian.org jessie InRelease [14.9 kB] Get:3 http://archive.raspberrypi.org jessie InRelease [22.9 kB] ... npm notice created a lockfile as package-lock.json. You should commit this file. added 166 packages in 81.166s Removing intermediate container 13986530db28 ---> 051eb94b8a3c Step 19/20 : EXPOSE 9876 ---> Running in 67b587845fe0 Removing intermediate container 67b587845fe0 ---> 46973b7ba9ac Step 20/20 : CMD ["npm", "start"] ---> Running in 153bf2ea02ad Removing intermediate container 153bf2ea02ad ---> 6bf3d76d38ae Successfully built 6bf3d76d38ae Successfully tagged oliv-nodepi:latest ed9a7d9042dddd3939b1788cf0e89d16f5273192a6456266507f072f90ce91bc $
$ docker run -p 9876:9876 -t -i --privileged -v /dev/ttyUSB0:/dev/ttyUSB0 -d oliv-nodepi:latestThen from a machine seeing the Raspberry PI on its network (it can be the Raspberry PI itself), reach http://raspi:9876/data/demos/gps.demo.wc.html in a browser.
$ docker run -it oliv-nodepi:latest /bin/bash # # ###### ### ## # #### ##### ###### # # # # # # # # # # # # # # # # # # # # # ##### ##### ###### # # # # # # # # # # # # ## # # # # # # # # # #### ##### ###### # ### git version 2.1.4 node:v9.11.1 npm:5.6.0 root@b9679d0d65a7:/workdir/node.pi#... and do whatever you like.
Docker, you do not deliver a software, you actually deliver an image (a virtual machine), on which a software is running.This is a SwitchDocLabs SDLWeather80422 Weather Station, installed on the roof.
It is connected to a Raspberry PI A+, all the software is written in Java, no Python, no Arduino-like code, no C++.
There is an optionalnodeJS server that runs on the Raspberry PI too, to enable WebSockets.
Find the core code here, and the example implementation here.
MySQL, PHP, Web Interface Web-Components interface, pings the server every second. WebSocket Web Interface, updated in real-time.See it live here.
|
It even comes with a pebble application. |
OpenCPN seems absurd to me. Such a program runs fine on a bigger device, with several gigabytes of RAM available.
iOS, Android, JavaFx, Swing all have UI rendering capabilities, but they're all totally different, and the learning curve for each of them is not always smooth.
HTML. Whatever OS runs on your laptop, tablet or smartphone (Windows, MacOS, iOS, Linux, Android, etc), you have a browser available, supporting HTML5 (if it does not, you should really upgrade it).
jQuery, ionic, ReactJS, ...) appear every day, and provide really rich and nice UI.
json format) is easily supported by a Raspberry PI, and the complexity of the UI rendering is 100% taken care of by the browser, running on a more powerful device.
/GET /tide-stations
/GET /tide-stations/{station}
/POST /tide-stations/{station}/wh?from=XXX&to=YYY
/POST /tide-stations/{station}/wh/details?from=XXX&to=YYY
this is the easy part - and then an HTML5/JavaScript User Interface.
I have been working on an NMEA Multiplexer that can run on small boards, like the Raspberry PI Zero. The code is available on github, see the documentation in the README.md.
It allows to mix all kinds of NMEA Sources into a single (or multiple) stream(s). You can read from Serial Ports, Log file(s), TCP, WebSocket, Sensors (like BME280, HTU21DF, LSM303, etc), merge those data and rebroadcast them on Serial port, TCP, Log file, WebSocket, GPSd, etc. UDP is being worked on.
Data can also be computed and injected in the output stream, like True Wind, Current direction and speed, etc.
As a graphical desktop can be cumbersome on small boards, the Multiplexer comes with a tiny HTTP server that provides a Web UI and REST services to allow remote Admin.
It also comes with several demos and samples This all works just fine with OpenCPN, SeaWi, that can take TCP streams as NMEA Data Input.?WATCH request. It is followed by a JSON Object like this:
?WATCH={"enable":true,"json":true}
... and here is the trick, OpenCPN sends a
?WATCH={"enable":true,"nmea":true}
This nmea option is "poorly" documented, but very useful. Instead of sending JSON objects, GPSd spits out the raw NMEA sentences, as they were read. Then GPSd is just a regular TCP stream, and OpenCPN already knows how to parse the NMEA sentences it delivers. This way, GPSd is not limited to strictly GPS-related sentences. It can convey all NMEA sentences, Boat Speed related, Wind related, etc. This is what the GPSd forwarder that comes with the Multiplexer is doing.
Happy Streaming, happy new year!
You can give a default position (in the preferences), and it will be used when no GPS Data is available.
2015-11-21-raspbian-jessie.img)PS: About the pinout: Put the SD Card on top, the biggest Raspberry design under the board. The pin #1 (3V3) is at the top left of the header.
-ue:olivsoftdesktopuserexits.FONAUserExit
import adafruiti2c.sensor.AdafruitBMP180
object Scala_101 {
def main(args: Array[String]) {
println("Hello, Scala world!")
val bmp180 = new AdafruitBMP180
try {
val temp = bmp180.readTemperature
val press = bmp180.readPressure / 100
println(s"CPU Temperature : ${SystemInfo.getCpuTemperature}\272C")
println(s"Temp:${temp}\272C, Press:${press} hPa")
} catch {
case ex: Exception => {
println(ex.toString())
}
}
}
}
#!/bin/bash # SCALA_HOME=/home/pi/.sbt/boot/scala-2.10.3 PI4J_HOME=/opt/pi4j # CP=$SCALA_HOME/lib/scala-library.jar # CP=$CP:$PI4J_HOME/lib/pi4j-core.jar CP=$CP:../AdafruitI2C/classes CP=$CP:./out/production/Scala.101 # sudo java -classpath "$CP" Scala_101
Hello,Scala world! CPU Temperature :40.6°C Temp:22.5°C, Press:1010.73 hPa
http://machine:port/html5/console.html, default port being 9999.
http://machine:9876/data/console.ws.html
http://machine:port/html5/admin.html, started when the console is in headless mode:
The default values on the pages above come from the Desktop Preferences.
olivsoftdesktop.PreferencesCLI. An entry will added soon in the User Interfaces.
HTML5 console |
HTML5 WebSocket console |
HTML5 Admin console |
theme and border. theme can be 'white' or 'black', border can be 'Y' or 'N'.
http://machine:9999/html5/console.html?theme=white&border=N
Prompt> sudo apt-get update Prompt> sudo apt-get install -y libgtk2.0-dev gettext git-core cmake gpsd gpsd-clients \ libgps-dev build-essential wx-common libwxgtk2.8-dev \ libglu1-mesa-dev libgtk2.0-dev wx2.8-headers \ libbz2-dev libtinyxml-dev libsdl1.2debian xcalib Prompt> git clone https://github.com/seandepagnier/OpenCPN.git Prompt> cd OpenCPN/ Prompt> mkdir build Prompt> cd build Prompt> cmake ../ Prompt> make Prompt> sudo make installAfter that, at the prompt you enter:
Prompt> opencpn &And that's it!