Приказивање постова са ознаком raspberry pi. Прикажи све постове
Приказивање постова са ознаком raspberry pi. Прикажи све постове

субота, 17. август 2019.

The ultimate read-only file system on the SD card

This is a kind of follow-up of my previous post:
https://mvidakovic.blogspot.com/2019/01/read-only-file-system-on-raspberry-pi.html

I have recently stumbled upon this tool:
https://github.com/BertoldVdb/sdtool

I immediately forked it:
https://github.com/milanvidakovic/sdtool

The main idea behind this tool is to set one of the following command bits of the CSD register (very low level communication) of the SD card (via SPI interface):

  • TMP_WRITE_PROTECT - temporarily enable/disable write protection for the SD card,
  • PERM_WRITE_PROTECT - permanently enable write protection for the SD card (cannot be reversed).
The behavior of the temporary write protection is quite interesting: when you enable it, it will silently fail when writing to the SD card. In reality, it means that when you try to write to a file (or create a new file), you will not get any error, and you might even be able to read that file (probably because it was placed in the cache of the file system), but as soon as you reboot, you will see that there have not been any changes.

How can this be used? First of all, you should set up your Raspberry Pi file system to read-only (for example, you can use this), so it would be normal not to have writes to the SD card. Then you can use the sdtool to enable the write protection. With those two steps, you should get the ultimate protection of your SD card.

Why is this important? I have couple of RPIs which work constantly for years. My experience with SD cards and RPIs is that they eventually corrupt the SD card. Sooner or later it will happen. If you just enable the read-only file system, it seems that the SD card still gets corrupted. I don't know why, but it happened to me several times. I have changed several SD cards (different vendors, different models), and several power adapters, and still got SD cards corrupted at some point. I thought that having the file system set to read-only would not corrupt the SD card during power loss (unattended shutdown), but, unfortunately, the SD card would get corrupted anyway. 

Even if the RPi is on the UPS (doesn't get turned off improperly), it would still corrupt the SD card sooner or later. It happened too many times with my RPIs (and the Odroid, too). 

One way to fix this is to boot the device from the USB hard disk instead of SD card, but then the same problem can occur with the hard disk (although, not that bad as it occurs with the SD card). As a matter of fact, it happened once with my Odroid and once with my RPi 3 (to have the USB hard disk corrupted). In addition, why would you put the USB hard disk on your RPI 0? It is supposed to be a lightweight system, not having a hard disk that consumes more power than RPI 0.

I have found an information on some forums that a special (quite expensive) SD card types do not get corrupted at all: aMLC and SLC type. Both are significantly more expensive than the usual cards, but people claim that they simply do not get corrupted.

One funny thing - my RPI 1 has corrupted the SD card only once in almost five years, while RPI Zero and RPI 2 and 3 can corrupt the SD card in a matter of months (or even sooner). Does it have to do with the speed of the system? Is the SD card overwhelmed with data and then it goes corrupt? Why is it corrupted in the read-only mode, then? I don't know answers to all those questions. What I do know, however, is the fact that the SD card would eventually get corrupted and that is the main reason for this post.

I am now testing this temporary write protection in conjunction with the read-only file system on my RPIs, and I will update this post with my experience.

At the end, just to repeat the fact that the contacts on the SD card are actually the SPI interface contacts, meaning that all the communication with the SD card goes over the SPI interface:




недеља, 14. октобар 2018.

Electronic art or Raspberry PI as a wall clock

Raspberry pi as a wall clock

In one of my posts, I have described how I have used old Android phones to show time and temperature. Both phones were connected to the power supply, and without a battery. (One of the highlights of that post was the manual how to remove the battery and yet have the phone working). In that post I was more worried about the display being turned on constantly, 365x24, than anything else. I thought that since I have removed the battery, the only thing that could break would be the screen, since it worked constantly, day and night.

Well, I was wrong. Both phones died (both managed to work that way for more than a year), but it was not the display, nor the motherboard or the CPU. The WiFi module in both phones died. One interesting fact is that one of those phones (Samsung Galaxy S plus) had the Super AMOLED capacitive touchscreen, and that screen did have a kind of burned pixels from my program. Not too noticeable, but existent.

When the first phone died, I had one spare RPI and one small touch screen for that RPI. I have written a small Swing Java program that connects to my weather server, pulls the data and displays that, just as the Android application on those dead phones. The program writes the information one pixel to the left every second, to save the pixels (not all pixels - those displaying the taskbar will be more burned). 

The result was - from this:
to this:


If you look carefully, you will notice that the display (and the RPI) is turned upside-down. That is because of the USB power cable, which tends to crack if it stands upright without any support (this RPI stands upright on its own). So, I have made one additional transformation of the picture to flip it. 

The small red rectangle at the lower right corner turns on and off each second to give me a visual indicator that the computer did not freeze (RPIs tend to freeze for too much reasons).

Then the second phone died. Again, I had another unused RPI and a 7-inch display purchased to be a portable HDMI monitor when I need to service my other RPIs permanently placed around the apartment. Well, that screen (and the RPI) has found its new purpose - to be a wall clock.

The only problem was that there were too many cables. I had 5V adapter which powers both RPI and the screen, the RPI and the screen. Two USB power cables and one HDMI cable.

So, instead of this phone:

I have created a kind of electronic art:


On the picture above, you can spot the power adapter at the lower left corner, RPI at the lower right corner, and the screen placed inside a portable LCD TV which died long time ago, but had the same-sized LCD display, so I was able to recycle the case.

Now, if you look closely at the picture, you will notice that the picture is not flipped. On this RPI, I don't have the problem with the power cable (everything was secured inside the frame), so I did not have to flip the picture. So I have introduced one additional configuration parameter: to flip the picture or not. 

The RPI connects to the weather server using WiFi. I plan to bring the Ethernet cable there as a next step.

петак, 3. август 2018.

Raspberry PI stuff

Various stuff about Raspberry Pi



Installation

You need to download the OS image from the official Raspberry PI site:


I prefer Raspbian with desktop.

Then you need to download the Etcher software for writing the OS image to the micro SD card:


Put the micro SD card in your computer, start the Etcher, choose the image file and write.

When everything is done, remove the micro SD card safely from the PC, put it in the Raspberry PI, connect HDMI cable (in case of Zero, mini HDMI cable) from RPI to the monitor (or TV), and connect the keyboard to one of the USB ports (in case of RPI Zero, you need to connect your USB keyboard via adapter to the micro USB port). Connect the power cable. RPI will boot for the first time.

Default username/password is pi/raspberry.

Upon login, start the raspi-config by typing:

sudo raspi-config

This will start the configuration utility for the RPI. I use it to set up the new password, host name of the RPI and to turn on almost all interfacing options. When setting the interfacing options, I turn on the SSH, I2C, SPI and 1-wire. 

When exiting, the raspi-config will reboot the machine.

I prefer to set up the static IP to my RPIs, so here are some combinations:
1. Set up RPI 3 with the static IP on Ethernet,
2. Setup RPI Zero with the static IP on wireless,
3. Set up RPI Zero with the Ethernet support (needs additional ENC28J60 module to be connected to the RPI Zero).

Setting up RPI 3 with the static IP on Ethernet (and WiFi)

Before booting, connect the Ethernet cable from your router to the RPI 3, and connect the power. You can then log on. From that moment, you can set up the static IP address. Before that, you can check if the networking works. First of all, you can type:

ifconfig

This will write your IP address, which your RPI obtained from the router (via DHCP). If the IP address of the RPI begins, for example, with 192.168.1, then the static IP address will need to start the same way (remember first three numbers of the IP address). 

Here we have two branches:
1. from stretch, on with the buster builds of the Raspbian
2. before stretch build.

Stretch, buster, and newer builds

To set up the static IP address, you need to edit the /etc/network/interfaces file:

sudo nano /etc/network/interfaces

The nano editor will open the interfaces file. You can then put the following content:

# interfaces(5) file used by ifup(8) and ifdown(8)

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

auto eth0
allow-hotplug eth0
iface eth0 inet manual

auto wlan0
allow-hotplug wlan0
iface wlan0 inet manual
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

Both eth0 and wlan0 (I have decided to assign my wlan0 static address, too) are set to manual. In case of wlan0, you need to edit the /etc/wpa_supplicant/wpa_supplicant.conf file to the basic content:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
   ssid="xxxx"
   psk="yyyy"
}

Then you need to add the following code to the end of the /etc/dhcpcd.conf file:

# Static eth0 IP configuration
interface eth0
static ip_address=192.168.1.207/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8
# Static wlan0 IP configuration
interface wlan0
static ip_address=192.168.1.217/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8

Before stretch (or buster) builds

To set up the static IP address, you need to edit the /etc/network/interfaces file:

sudo nano /etc/network/interfaces

The nano editor will open the interfaces file. You can then put the following content:

# interfaces(5) file used by ifup(8) and ifdown(8)

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

allow-hotplug eth0
iface eth0 inet static
address 192.168.1.200
netmask 255.255.255.0
gateway 192.168.1.1

The address set in this example is 192.168.1.200. After that, you can restart the networking by typing:

sudo service networking restart

Or, you can reboot the RPI by typing:

sudo reboot


Setting up RPI Zero with the static IP on Wireless

RPI Zero W already has the wireless, while RPI Zero does not. In case of having the RPI Zero, you need to obtain WiFi dongle and some adapter to connect it to the micro USB port. After that, the procedure is the same for both RPI Zero W and RPI Zero.

Here too, we have two branches:
1. stretch/buster builds.
2. pre-stretch(or buster) builds

Stretch, buster, and newer builds

Just look above at the same title.

Before stretch (or buster) builds

You need to edit the /etc/network/interfaces by typing:

sudo nano /etc/network/interfaces

In the nano editor, change the interfaces file to:

# interfaces(5) file used by ifup(8) and ifdown(8)

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

allow-hotplug wlan0
iface wlan0 inet static
#    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
        wpa-ssid "MySSID"
        wpa-psk "xxxxxx"
address 192.168.1.201
netmask 255.255.255.0
gateway 192.168.1.1

The address set in this example is 192.168.1.201. The MySSID is the SSID of your WiFi network. You must enter the SSID and the password with the quotes (").


Setting up RPI Zero for the Ethernet support

RPI Zero supports the ENC28J60 Ethernet module out of box.

ENC28J60 Ethernet module

This module needs to be connected to the RPI Zero via SPI interface. Don't forget to enable the SPI from the raspi-config tool (look above). After that, you need to do the following:

1. Connect the ENC28J60 module to the RPI using the following pin scheme:

Pi            PinNo ENC28J60     
---------------------------------
+3V3          17 VCC          
GPIO10/MOSI    19 SI           
GPIO9/MISO    21 SO           
GPIO11/SCLK    23 SCK          
GND            20 GND          

GPIO25        22 INT          
CE0#/GPIO8    24 CS           

2. Enable the ENC28j60 module at the end of your /boot/config.txt file by typing:

sudo nano /boot/config.txt

This will open the nano editor. Go to the end of the file and enter the following text:

dtoverlay=enc28j60

3. Reboot (sudo reboot)

From this moment on, you can work with the Ethernet as eth0 device.


Having static IP on both Ethernet and WiFi

The text below is for the pre-stretch/buster builds. For having both ethernet and WiFi static, look above, at the "Setting up RPI 3 with the static IP on Ethernet (and WiFi)" title.

If you want to have the static IP on both Ethernet port and WiFi, you need to edit the /etc/network/interfaces file and put the following text:

# interfaces(5) file used by ifup(8) and ifdown(8)

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

#allow-hotplug eth0
iface eth0 inet static
address 192.168.1.202
netmask 255.255.255.0
gateway 192.168.1.1

auto wlan0
#allow-hotplug wlan0
iface wlan0 inet static
#    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
        wpa-ssid "MySSID"
        wpa-psk "xxxxxxx"
address 192.168.1.212
netmask 255.255.255.0
gateway 192.168.1.1

The address set in this example for the Ethernet is 192.168.1.202 and for the WiFi is 192.168.1.212. 

Installing Java8 on your RPI

Type the following in your console:

sudo aptitude install oracle-java8-jdk

This will install the Java8 installer and would run it. 


Samba support

Samba allows you to share a part of your RPI disk to the network, for other machines and users. It also allows you to access other samba shares on the network. We will focus on the sharing of our disk on the network.

Install Samba via apt-get:

sudo apt-get install samba samba-common-bin

Edit the smb.conf file using nano:

sudo nano /etc/samba/smb.conf

Find the entries for workgroup and wins support, and set them up as follows:

workgroup = your_workgroup_name
wins support = yes

You also need to add the following section to end of the smb.conf to add share:

[pihome]
   comment= Pi Home
   path=/home/pi
   browseable=Yes
   writeable=Yes
   only guest=no
   create mask=0777
   directory mask=0777
   public=no

This will add the Samba share named "pihome" on your RPI, so it will be accessible from other machines.

At the end, we need to add the current user to the Samba:

sudo smbpasswd -a pi

After that, just restart the smbd daemon:

sudo systemctl restart smbd


четвртак, 10. мај 2018.

Generating VGA video signals using Raspberry Pi and then FPGA

I have recently found couple of posts (for example, this and this) on the net about generating VGA signals using just CPU. All those posts talk about generating 640x480 VGA video signals using Arduino.
I have decided to try to generate VGA signals using Raspberry PI 3. I have read that you can actually get the VGA output from the RPI without much problems, since it has that feature built in the Broadcom SoC. However, I didn't want to use the built in feature; I wanted to generate signals myself.
It proved to be quite difficult. First I tried the lowest possible level library, under the Raspbian OS:

// Set GPIO pins to output
INP_GPIO(VIDEO); // must use INP_GPIO before we can use OUT_GPIO
OUT_GPIO(VIDEO);
INP_GPIO(H_SYNC); // must use INP_GPIO before we can use OUT_GPIO
OUT_GPIO(H_SYNC);
INP_GPIO(V_SYNC); // must use INP_GPIO before we can use OUT_GPIO
OUT_GPIO(V_SYNC);

vSyncLow();
hSyncLow();
vgaOff();
nsleep(99999999);
while(1) {
line = 0;
vSyncLow();

while(line < 600){
    //2.2uS Back Porch
    delayMicroseconds(2);  
    
    //20uS Color Data
    vgaOn();  //High
    delayMicroseconds(10); // 1uS        
    //Red Color Low
    vgaOff();  //Low
    delayMicroseconds(10); // 1uS        
    
    //1uS Front Porch
    delayMicroseconds(1); // 1uS 
    line++;
    
    //3.2uS Horizontal Sync
    hSyncHigh();  //HSYNC High
    delayMicroseconds(3);
    hSyncLow();  //HSYNC Low
    
    //26.4uS Total
  }
  //Clear the counter
  line=0; 
  //VSYNC High
  vSyncHigh();
  //4 Lines Of VSYNC   
  while(line < 4){         
    //2.2uS Back Porch    
    delayMicroseconds(2);
    
    //20 uS Of Color Data
    delayMicroseconds(20);// 20uS
    
    //1uS Front Porch
    delayMicroseconds(1); // 1uS
    line++;
    
    //HSYNC for 3.2uS
    hSyncHigh();  //High
    delayMicroseconds(3);
    hSyncLow();  //Low  
    
    //26.4uS Total
  }
  
  //Clear the counter
  line = 0;
  //VSYNC Low
  vSyncLow();
  //22 Lines Of Vertical Back Porch + 1 Line Of Front Porch
  while(line < 22){
      //2.2uS Back Porch
      delayMicroseconds(2);

      //20uS Color Data
      delayMicroseconds(20);// 20uS
        
      //1uS Front Porch
      delayMicroseconds(1); // 1uS
      line++;
      
      //HSYNC for 3.2uS
      hSyncHigh();  //High
      delayMicroseconds(3);
      hSyncLow();  //Low  

      //26.4uS Total
  }     
}

The result was this:


The monitor has some physical damage, but that is not the problem. The problem is the bad synchronization. The timing is critical. Since the pixel frequency is approx. 25MHz, the time for a single pixel is 40 nanoseconds. In the picture, it is obvious that the lines do not start at the same time, which means that the horizontal sync pulses do not start at the precise time. They miss their time for couple of hundred of nano seconds.

OK, this could be due to the multitasking in the Linux OS. So, I moved to the bare metal programming. I have found a nice bare metal library on the github, here:


Author did a great job of making nice, readable examples. I have modified one of his examples and made a VGA signal generator using the built in interrupt generator, which is triggered every 3 microseconds:

// clear pending irq
*ARMTIMER_ACQ = 1;
//*TIMER_BASE = 2;
//printf("Inside dbg_main, counter: %d ", counter);
if (counter == 0) {
GPIO_SET(V_SYNC);
GPIO_SET(H_SYNC);
  GPIO_SET(VIDEO);
} else  
if (counter == 1) {
GPIO_CLR(H_SYNC);
  GPIO_CLR(VIDEO);
} else 

if ((counter % 8) == 0) {
GPIO_SET(H_SYNC);
  GPIO_SET(VIDEO);
} else 
if ((counter % 8) == 1) {
  GPIO_CLR(VIDEO);
GPIO_CLR(H_SYNC);

if (counter == 17) {
GPIO_CLR(V_SYNC);
}

counter++;
if (counter == 4700) {
counter = 0;
}

However, the picture was not much better:


The timing is a bit better, but still horizontal sync pulses manage to miss the right time to fire.

I did all of this while waiting for the DE0-NANO FPGA board, which I choose to play with in order to generate VGA signals. When it finally arrived, I was able to properly generate VGA signals:


Then I have added the color and some text (top left corner):


Damage on the LCD is visible here, but it is OK for the development.

Here is the Altera DE0-NANO FPGA board:


I have purchased a female VGA connector and connected GPIO pins from the board to the connector:
  • GPIO_R -> 68Ohm -> VGA_R
  • GPIO_G -> 68Ohm -> VGA_G
  • GPIO_B -> 68Ohm -> VGA_B
  • GPIO_HS -> 470Ohm -> VGA_HORIZONTAL_SYNC
  • GPIO_VS -> 470Ohm -> VGA_VERTICAL_SYNC

I have found various values for the resistors on various sites (from direct connections, to 68 Ohms, 100Ohms, 500 Ohms, etc.), but this schematics works for me.

The Verilog code is quite simple. I have recycled the FizzBuzz example made by Ken Shirriff:

module vga(
//////////// CLOCK //////////
input CLOCK_50,  // this is 50MHz clock
//////////// KEY //////////
input KEY,             // reset key (one of two onboard keys)
//////////// GPIO //////////
output reg r,
output reg g,
output reg b,
output wire hs,
output wire vs
);

//=======================================================
//  REG/WIRE declarations
//=======================================================
reg clk25; // 25MHz signal (clk divided by 2)
reg newframe;
reg newline;

reg [9:0] x;
reg [9:0] y;
wire valid;

reg [7:0] xx;
reg [7:0] yy;

reg [7:0] framebuffer [9:0]; // 10  bytes text-based framebuffer
wire [6:0] counter;
wire [7:0] pixels; // Pixels making up one row of the character
//////////// GPIO //////////
output reg r,
output reg g,
output reg b,
output wire hs,
output wire vs
);
//=======================================================
//  REG/WIRE declarations
//=======================================================
reg clk25; // 25MHz signal (clk divided by 2)
reg newframe;
reg newline;

reg [9:0] x;
reg [9:0] y;
wire valid;

reg [7:0] xx;
reg [7:0] yy;

reg [7:0] framebuffer [9:0];
wire [6:0] counter;
wire [7:0] pixels; // Pixels making up one row of the character

//=======================================================
//  Structural coding
//=======================================================
initial begin
framebuffer[0] = "0";
framebuffer[1] = "1";
framebuffer[2] = "2";
framebuffer[3] = "3";
framebuffer[4] = "A";
framebuffer[5] = "a";
framebuffer[6] = "B";
framebuffer[7] = "b";
framebuffer[8] = "8";
framebuffer[9] = "9";
end
// Character generator

chars chars_1(
  .char(framebuffer[counter]),
  .rownum(y[2:0]),
  .pixels(pixels)
  );

assign hs = x < (640 + 16) || x >= (640 + 16 + 96);
assign vs = y < (480 + 10) || y >= (480 + 10 + 2);
assign valid = (x < 640) && (y < 480);
assign counter = (valid)?(x >> 3):0;

always @(posedge CLOCK_50) begin
newframe <= 0;
newline <= 0;
if (!KEY) begin
x <= 10'b0;
y <= 10'b0;
clk25 <= 1'b0;
newframe <= 1;
newline <= 1;
end
else begin
clk25 <= ~clk25;
if (clk25 == 1'b1) begin
if (x < 10'd799) begin
x <= x + 1'b1;
end
else begin
x <= 10'b0;
newline <= 1;
if (y < 10'd524) begin
y <= y + 1'b1;
end
else begin
y <= 10'b0;
newframe <= 1;
end
end
end
end

if (valid) begin

if (x < 80 && y < 8) begin
r <= pixels[7 - (x & 7)];
g <= pixels[7 - (x & 7)];
b <= pixels[7 - (x & 7)];
end
else begin
r <= (x < 213) ? 1 : 0;
g <= (x >= 213 && x < 426) ? 1 : 0;
b <= (x >= 426) ? 1 : 0;
end
end
else begin
// blanking -> no pixels
r <= 0;
g <= 0;
b <= 0;
end
end
endmodule

Verilog programming is not simple. It has a steep learning curve. The other problem can be a long compile time in the Quartus II IDE. For a bit more complex code than this VGA project, the compile time easily exceeds couple of minutes. I have solved this problem by installing the Icarus Verilog software, which compiles the Verilog code in a fraction of a second. This is due to the fact that the Icarus Verilog is not intended to deploy the Verilog code to the actual hardware - instead, it is intended for the simulation only. This way, I am able to produce the running and correct code quickly, and then I can copy that code into the Quartus II IDE, build the project, and deploy it to the real hardware.

недеља, 26. новембар 2017.

More sensors

This is a followup of my previous post.
I have managed to integrate multiple sensors into a single system capable of sensing if the entrance door is locked/unlocked, if there is a motion inside the apartment and to record the temperature and humidity.
It all started with the status of the entrance door. I wanted a system capable of sensing the status of the lock, but to be able to work without any electrical contacts, since it may corrupt the sensor in time. So I have found an inductive (contactless) switch and I have placed it in the entrance door of my apartment. It is connected to one of my raspberry pi computers, and it sends the information about the state of the door lock to my main server.
The Pi is glued to the wall next to the door:
You may wonder why are the headphones attached to the Pi. They emit the annoying sound of sine waveform, frequency of 3000Hz when the door is unlocked for more than one minute. That way I have a reminder that I need to lock the door.
The red LED is an indicator of the door lock status. If it is red, the door is locked. However, going to the door to see if it is locked or unlocked is not too attractive. I had to invent some way to know the door status without actually going to the door. That is why I have developed the Android application for it:
The lock icon goes red when the door is locked (the time is written below the icon), and goes green when the door is unlocked. The notification comes in real time thanks to the Google Cloud Messaging.
Next came the web portal. I wanted to have the history of door locking and unlocking. That is how I made this web page:

The red cell in the table indicates that there was some motion detected after the door has been locked. This is the proper introduction for the motion detection sensors: after adding door lock and temperature sensors, I wanted to add motion detection sensors inside the apartment. I have used HC SR-501 PIR motion detector:
I have three sensors placed around the apartment and I have a web page which updates the motion information in real time (WebSockets used):

Let us not forget the temperature and humidity sensors:


недеља, 20. август 2017.

Added telemetry to the toy car

This is a followup of my original post.

I have added a voltage readout of the battery in my toy car. It looks like this on client applications:
Java Swing application

Android application

Voltage is read using MCP 3008 A/D converter:
MCP 3008 A/D converter pinout

Here is the layout:

  • connect pins 16 (Vdd) and 15 (Vref) to the 3.3V pin of your GPIO port of your Orange Pi, or Raspberry Pi (they are pin-to-pin compatible),
  • connect pins 14 (Agnd) and 9 (Dgnd) to the GND pin of your GPIO port,
  • connect pin 13 (CLK) to the pin 23 of the GPIO port,
  • connect pin 12 (Dout) to the pin 21 of the GPIO port,
  • connect pin 11 (Din) to the pin 19 of the GPIO port,
  • connect pin 10 (CS) to the pin 24 of the GPIO port.
Make sure that SPI is enabled on your device. On my Orange Pi, it is was enabled by default. On Raspberry Pi, you need to start the raspi-config and enable SPI from the menu.
If the SPI is properly enabled, you will be able to see two files in the /dev folder of your Pi:
/dev/spidev0.0 and /dev/spidev1.0.

I have used Adafruit MCP library from this location.

When you download and install this library, you will use it by importing following modules:
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008 

Then you need to set it up:
# Hardware SPI configuration:
SPI_PORT   = 1
SPI_DEVICE = 0
mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE)) 

On my Raspberry Pi, I have set the SPI_PORT variable to zero (0). On my Orange Pi, I had to set it to one (1). You can then read the voltage this way:
voltage = mcp.read_adc(0) 

The zero argument from the read_adc function is the number of the channel. In my case, I have connected the voltage from the battery to the pin 1 of the MCP3008, which is channel 0. Since the voltage of the fully charged battery can exceed 10V, I had to reduce the voltage:
Voltage reduction

I have extended my original Python server for the toy car to accept a telemetry request (string 't' as a telemetry request) and to send the voltage (currently it is just the voltage), as a JSON string:
elif text == 't':
voltage = mcp.read_adc(0)
c.send("{ \"voltage\": " + str(voltage) + '}\n')  

My client applications (both Java Swing and Android) send periodically this request string and receive a JSON string. Then they parse the JSON string, extract the voltage, and display it on the screen.