Raspbian Python3 control Wemos D1 Mini ESP8266

9 Dec

I had been using Arduino IDE from Raspbian to test simple GPIO write() to turn on and off an led. Then I found pymata_express that creates a communication framework on the Arduino device. The catch was how to test it? Best fun is to use CLI. Here is a sample code that established the communication:
pi@raspberrypi:~/MicroPython_ESP8266 $ python3
Python 3.7.3 (default, Apr 3 2019, 05:39:12)
[GCC 8.2.0] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>>
>>> import asyncio
>>> from pymata_express.pymata_express import PymataExpress
>>> board = PymataExpress()

Pymata Express Version 1.6
Copyright (c) 2018-2019 Alan Yorinks All rights reserved.

Opening all potential serial ports…
/dev/ttyUSB0

Waiting 4 seconds(arduino_wait) for Arduino devices to reset…

Searching for an Arduino configured with an arduino_instance = 1

Arduino found and connected to /dev/ttyUSB0

Retrieving Arduino Firmware ID…
Arduino Firmware ID: 2.5 FirmataExpress.ino
Auto-discovery complete. Found 18 Digital Pins and 1 Analog Pins

>>>
That response on the last line gave me the confidence to download the sample code from https://github.com/MrYsLab/pymata-express/blob/master/examples/digital_output.py
To match the requirement of my Wemos D1 Mini, I just modified code on line 51:
loop.run_until_complete(blink(board, 9))
to:
loop.run_until_complete(blink(board, 2))
That shows the LED on board and also my external led was connected to GPIO 2 or D4. (check that in Wemos docs)
Now run the sample code:
pi@raspberrypi:~/MicroPython_ESP8266 $ python3 pymata_test_blibk.py

Pymata Express Version 1.6
Copyright (c) 2018-2019 Alan Yorinks All rights reserved.

Opening all potential serial ports…
/dev/ttyUSB0

Waiting 4 seconds(arduino_wait) for Arduino devices to reset…

Searching for an Arduino configured with an arduino_instance = 1

Arduino found and connected to /dev/ttyUSB0

Retrieving Arduino Firmware ID…
Arduino Firmware ID: 2.5 FirmataExpress.ino
Auto-discovery complete. Found 18 Digital Pins and 1 Analog Pins

ON
OFF
ON
OFF
ON
OFF
ON
OFF
pi@raspberrypi:~/MicroPython_ESP8266 $

Now I am ready for some Robotics using ESP8266!!
Here are the goodies:
1. You are using the new Python 3 I have Python 3.7.3
2. pymata_express uses ZMQ and associated libraries, which will be good for learning.
3. Need to learn how to accomplish this using WiFi (I have not yet tried)

Arduino on ESP32 with Raspberry Pi and MQTT

14 Apr

I have one ESP32. Connected to my Arduino development on laptop. I had forgotten how I got the IP address for ESP32.So this is to add to my notes, so that I can recollect it again. You see I am 78, and do forget these things!!
So, the easy step is, to connect the ESP32 to my /dev/USB0 and from Arduino IDE, select Tools, Serial Monitor. Initially it was at 9600 baud and showed garbage. Changed the setting to 115200 and got proper messages.
Jun 8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1100
load:0x40078000,len:10312
load:0x40080400,len:6460
entry 0x400806a4

Connecting to jpm_home
……..
WiFi connected
IP address:
192.168.0.6
Attempting MQTT connection…connected
Publish message: person detected #1
Publish message: person detected #2
Publish message: person detected #3
Publish message: person detected #4

Here I can see the IP address of 192.168.0.6. So, I could ping it also. But it did not show in Router Client Lists. Why?
My PIR sensor attached to ESP32 gave signals, which were captured by the ESP32 MQTT Client and sent to MQTT Server running on 192.168.0.9 on Raspberry Pi.
mosquitto_sub -v -t ‘outTopic’ showed the PIR sensor messages.

Tesseract Training Data

13 Apr

After you have installed Tesseract, you could see the tessdata in folder :

jpm@skylab /usr/share/tesseract-ocr/4.00/tessdata $ ls -al
total 25376
drwxr-xr-x 4 root root 4096 Mar 31 16:27 .
drwxr-xr-x 3 root root 4096 Mar 30 13:03 ..
drwxr-xr-x 2 root root 4096 Apr 6 11:34 configs
-rw-r–r– 1 root root 15400601 Mar 31 16:27 eng.traineddata
-rw-r–r– 1 root root 10562727 Sep 16 2017 osd.traineddata
-rw-r–r– 1 root root 572 Apr 7 2018 pdf.ttf
drwxr-xr-x 2 root root 4096 Mar 30 13:03 tessconfigs

Here by default it had eng.traineddata of size 4113088. This was the fast dictionary. But another one for better accuracy is of size 15400601. This will take a bit more processing time, but produces a little better accuracy.

If you read the FAQ on this project it mentions Directed Acyclic Word Graph (DAWG). You can search for some graphical depiction of DAWG in the Internet. You could also see that Tessearct uses Long Short-Term Memory (LSTM) and Recurrent Neural Network. I needed a way to see what is in this magic data. Along with Tesseract installation, it gives you two more interesting utilities. One combine_tessdata that can zip and unzip the eng.traineddata. Here I have user option -u to unpack the data. Now you can see the various DAWG files in that.

jpm@skylab ~/tesseract-python/extract_data $ combine_tessdata -u original_eng.traineddata ./tmp
Extracting tessdata components from original_eng.traineddata
Wrote ./tmp.lstm
Wrote ./tmp.lstm-punc-dawg
Wrote ./tmp.lstm-word-dawg
Wrote ./tmp.lstm-number-dawg
Wrote ./tmp.lstm-unicharset
Wrote ./tmp.lstm-recoder
Wrote ./tmp.version
Version string:4.00.00alpha:eng:synth20170629
17:lstm:size=401636, offset=192
18:lstm-punc-dawg:size=4322, offset=401828
19:lstm-word-dawg:size=3694794, offset=406150
20:lstm-number-dawg:size=4738, offset=4100944
21:lstm-unicharset:size=6360, offset=4105682
22:lstm-recoder:size=1012, offset=4112042
23:version:size=30, offset=4113054
jpm@skylab ~/tesseract-python/extract_data $ ls -al
total 8068
drwxr-xr-x 2 jpm jpm 4096 Apr 13 17:22 .
drwxr-xr-x 15 jpm jpm 4096 Apr 13 17:16 ..
-rw-r–r– 1 jpm jpm 0 Apr 13 17:19 eng.config
-rw-r–r– 1 jpm jpm 0 Apr 13 17:19 eng.unicharset
-rw-r–r– 1 jpm jpm 4113088 Apr 13 17:15 original_eng.traineddata
-rw-r–r– 1 jpm jpm 401636 Apr 13 17:22 tmp.lstm
-rw-r–r– 1 jpm jpm 4738 Apr 13 17:22 tmp.lstm-number-dawg
-rw-r–r– 1 jpm jpm 4322 Apr 13 17:22 tmp.lstm-punc-dawg
-rw-r–r– 1 jpm jpm 1012 Apr 13 17:22 tmp.lstm-recoder
-rw-r–r– 1 jpm jpm 6360 Apr 13 17:22 tmp.lstm-unicharset
-rw-r–r– 1 jpm jpm 3694794 Apr 13 17:22 tmp.lstm-word-dawg
-rw-r–r– 1 jpm jpm 30 Apr 13 17:22 tmp.version
jpm@skylab ~/tesseract-python/extract_data $

Now to run dawg2wordlist you need eng.unicharset file, which can be copied from here:
https://raw.githubusercontent.com/bergwolf/tesseract-ocr-copy/master/tessdata/eng.unicharset
Now run dawg2wordlist as follows:
jpm@skylab ~/tesseract-python/extract_data $ dawg2wordlist ./eng.unicharset ./tmp.lstm-word-dawg ./eng.lstm-word.txt
Loading word list from ./tmp.lstm-word-dawg
Reading squished dawg
Word list loaded.
Check the word list that has been created:
jpm@skylab ~/tesseract-python/extract_data $ ls -al eng.lstm-word.txt
-rw-r–r– 1 jpm jpm 3435334 Apr 13 21:18 eng.lstm-word.txt

Here is some lines from eng.lstm-word.txt
gHEEuegwUu
gHEEueswF
gHEEuesvFU
gHEEue¢wEu
gHEEuU
gHEEwFi

Cannot make head or tail of that…
So need to study more.
Joseph Mathew

Raspberry Pi projects build with Python3

10 Feb

The new Raspberry Pi 3B+ is a wonderful system for learning Python 3 programming as well as to build some simple projects that make use of GPIO pins. At $35 it is affordable, and has good features. The WiFi is so reliable, compared to the earlier models, that we can easily configure the Pi as a VNC Server running headless and being controlled from my laptop.

As part of set of systems for demonstrating the GPIO and Python libraries to connect to that, I needed VNC setup. By mistake, I installed tightvncserver first. It worked, but when I realized that the default raspi-config uses Real-VNC-Server, I quickly changed and setup real-vnc-server. There was a bit of issue regarding screen resolution, as I did not want an HDMI monitor connected to the Pi. Finally I agreed to the default Display resolution.

Next of key importance was ability to run Jupyter notebook using Python3. The default version I had was Python 3.5.3, but when I installed Jupyter and run any script, it could not display the output. Since many others had reported similar problems, I decided to update Python 3 to current 3.7.2 version. Practically, dropped X11 completely and reinstalled Python 3.7.2 and reinstalled raspberri-ui-mods, xinit, xutils and chromium-browser. Now I installed Jupyter again, and this time it worked fine. I am happy with my simple clean Desktop with Chromium Browser and Python 3.7.2.

Now I plan to test the various GPIO libraries and test some simple experiments to measure temperature, humidity etc. This should be elegant for a simple Python3 workshop using Raspberry Pi 3.

When I compare other single board systems like Radxa Rock Pro and Odroid etc, I am finding the Raspberry Pi 3B+ as a better solution for school students as a learning platform.

 

Rust shadowing and memory usage

31 Jan

Rust is an interesting language for its ability to create code that is strict. The recent announcement from AWS, that it would use Firecracker for running Serverless Functions, brings its reliability to highest level in production. I am a beginner and am exploring the marvels of this new language.

The Rust book chapter on Variables and Mutability gives some interesting examples. The topic on Shadowing shows this example:

fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!(“The value of x is: {}”, x);
}
I modified this code to be compiled with gdb using -g option, and also printed the address locations of x. This showed that all three variables are using different stack locations. This I felt is a waste of memory. So if shadowing does not have any purpose other than to use the same variable names, it could be better to use mut option.

Here is part of the code I used:

use std::mem;
use std::slice;
fn main() {
let x = 5;
println!(“{:p} {:?} “, &x, x);
let x = x + 1;
println!(“{:p} {:?} “, &x, x);
let x = x * 2;
println!(“{:p} {:?} “, &x, x);
println!(“The value of x is: {}”, x);

/*
gef➤ print x
$5 = 0xc
gef➤ print &x
$6 = (i32 *) 0x7fffffffd7e4
// the third allocation 6 * 2 = 12 (hex 0c)
gef➤ hexdump byte 0x00007fffffffd7e4 L32 UP
0x00007fffffffd7e4 0c 00 00 00 a0 99 1a f7 ff 7f 00 00 a0 db ff ff …………….
0x00007fffffffd7f4 ff 7f 00 00 0a c7 de f7 ff 7f 00 00 00 a3 fd f7 …………….
// the second allocation of x with a vaue of 5 + 1 = 6
gef➤ hexdump byte 0x00007fffffffd764 L32 UP
0x00007fffffffd764 06 00 00 00 80 40 78 55 55 55 00 00 03 00 00 00 …..@xUUU……
0x00007fffffffd774 00 00 00 00 58 99 57 55 55 55 00 00 02 00 00 00 ….X.WUUU……
// the first allocation of x with a vaue of 5
gef➤ hexdump byte 0x00007fffffffd6e4 L32 UP
0x00007fffffffd6e4 05 00 00 00 80 40 78 55 55 55 00 00 03 00 00 00 …..@xUUU……
0x00007fffffffd6f4 00 00 00 00 58 99 57 55 55 55 00 00 02 00 00 00 ….X.WUUU……
gef➤
*/

A similar issue is associated with the code:

let spaces = ” “;
let spaces = spaces.len();

The article clearly shows that it creates a new variable. But it can be easily missed.

“This construct is allowed because the first spaces variable is a string type and the second spacesvariable, which is a brand-new variable that happens to have the same name as the first one, is a number type. ”

So to confirm I did a gdb on the following code:
/* continuation of the above Rust code */

// spaces will be allocated on heap
// verified using gdb
let spaces = “aabbccddeeff”;
println!(“{:?}”, “Print address of spaces: “);
println!(“{:p} “, *&spaces);
println!(“The value of spaces is: {}”, spaces);
// print part of stack memory starting at spaces
println!(“{:?}”, “Printing heap memory from &spaces”);
let m: &[u8] = unsafe {
//slice::from_raw_parts(&a as *const _ as *const u8, 4 + 24 + 24)
slice::from_raw_parts(&spaces as *const _ as *const u8, 32)
};
println!(“{:X?}”, m);

// this var spaces will be allocated on stack.
// verified using gdb
let spaces = spaces.len();
println!(“{:?}”, “Print address of spaces: “);
println!(“{:p} “, &spaces);
// If you derefrence this pointer you get the basic value of 0xc ie 12
println!(“{:?} “, *&spaces);
println!(“The value of spaces is: {}”, spaces);
// print part of stack memory starting at spaces
println!(“{:?}”, “Printing heap memory from &spaces”);
let m: &[u8] = unsafe {
//slice::from_raw_parts(&a as *const _ as *const u8, 4 + 24 + 24)
slice::from_raw_parts(&spaces as *const _ as *const u8, 32)
};
println!(“{:X?}”, m);

}

/*

“Print address of spaces: ”
// This shows it is a heap address, the stack would be starting with 0x7fffff…
0x559d74cffb98
The value of spaces is: aabbccddeeff
“Printing heap memory from &spaces”
[98, FB, CF, 74, 9D, 55, 0, 0, C, 0, 0, 0, 0, 0, 0, 0, F0, A0, F0, 74, 9D, 55, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]
The first 8 bytes shown above is the little endian address of spaces on heap:
The actual address is : 0x0000559d74cffb98

Now we are printing the spaces after computing the length:
“Print address of spaces: ”
// This shows a stack address:
0x7fff36faf968
// If you derefrence it we get the basic value of 12
12
The value of spaces is: 12
“Printing stack memory from &spaces”

The value of C shown as the first character is in hex the length of 12
[C, 0, 0, 0, 0, 0, 0, 0, F0, A0, F0, 74, 9D, 55, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 58, FB, CF, 74, 9D, 55, 0, 0]
jpm@skylab ~/projects/test_shadow $
*/

This clearly shows the first String spaces was created on heap and the second variable spaces was created on the stack. The question is do we need to waste memory on the heap just to avoid counting the number of spaces? or Can we release the heap memory block? I need to learn how to do it.

 

 

Maintaining the status using SQLite3

26 Jan

The Process Monitoing Station using a combination of PI Zero or Pi3 for Client and a Pi3 for Server maintains the status of each Station using three Swtches and corresponding LED. When an alert is raised the user presses the Red Switch. The support person will get the message, and come and press the Yellow Switch to indicate start of the service and when completed, she will press the Green Switch. The Server gets each state transition and maintains the status in a SQLite3 database.

Since the Client is not critical we do not provide a battery backup for this system. So, in case a power failure occurs, and later when the Station comes back live, it needs a way to identify, what the last state was. We can get this from the server. So, during boot up, the Client Station will start in Waiting state and sent the ‘w’ status to the Server, which will respond back with the last status. This part of the code is shown below:

def get_last_status(hostname):
db = get_db()
cur = db.cursor()
cur.execute(“SELECT status FROM pmsstatus WHERE hostname = hostname \
ORDER BY id DESC LIMIT 1”)
status = cur.fetchone()[0]
mylogger.info(status)
# print(status)
return status

For checking the status of each Station we can use a Flask application which will fetch the data from the SQLite database using a Read Only connection.

 

Pi Zero FSM using ZeroMQ

26 Jan

Here is part of the code from Client which defines a PMS Class:

class PMS(object):
# Define some states. We expect W, R, Y and G
# these states define Wait, Red, Yellow and Green states
# Red means maintenance required
# Yellow means maintenance team in action
# Green means the system is back in operation.
# defined states = [‘w’, ‘r’, ‘y’, ‘g’]

# Initialize the state machine starts with Wait until Server gives state
def __init__(self, name):
self.name = name
self.machine = Machine(model=self, states=PMS.states, initial=’w’)

def update_journal(self):
# log message
pass

# Following methods will be invoked when FSM enter R, Y, G
# These are defined on the States
def enter_wait(): print(“in Wait State”)

def enter_red(): print(“in Red State”)

def enter_yellow(): print(“in Yellow State”)

def enter_green(): print(“in Green State”)

# Assign plasma1 as the Process Minitoring System with FSM features

plasma1 = PMS

hostname = os.uname().nodename

# The states
states = [
State(name=’w’, on_enter=[‘enter_wait’]),
State(name=’r’, on_enter=[‘enter_red’]),
State(name=’y’, on_enter=[‘enter_yellow’]),
State(name=’g’, on_enter=[‘enter_green’])
]
# Initial state will be w means waiting
# We will use the reply from Server to find last status
transitions = [
{‘trigger’: ‘w’, ‘source’: ‘*’, ‘dest’: ‘w’},
{‘trigger’: ‘r’, ‘source’: ‘*’, ‘dest’: ‘r’},
{‘trigger’: ‘y’, ‘source’: ‘*’, ‘dest’: ‘y’},
{‘trigger’: ‘g’, ‘source’: ‘*’, ‘dest’: ‘g’}
]

# Initialize
machine = Machine(plasma1, states=states, transitions=transitions, initial=’w’)

print(“FSM in action”)

 

Raspberry Pi based alert system

26 Jan

20171205_181453

The above image shows simple plastic box with Raspberry Pi Zero with GPIO wired to three push button switches with LED. It has a simple ZeroMQ Python script monitoring the satte of the Switches as a Finite State Machine. The status is transferred, everytime the switch is pressed, to a central server that runs the ZeroMQ pair and gives the acknowledgement as a Response object back. The Client will update the status of the LED so that we have a physical indicator.

The Server is also based on Raspberry Pi, but it uses a Pi3 with an Eth0 iterface connected to wired DHCP router, configured to use the static IP of 192.168.0.240. So the client Python script uses the local DHCP address and connects to the statis IP of the server. In the Server we have a SQLite3 database named pmsstatus (named for Process Monitoring Station Status) as the table. Each client is configured to use the hostname as the unique identification from where the Process is being monitored. In a factory for a large number of Plama Cutters and other machines, you could name them in a four or six character standard for ease of identification of the location.

So, for example I have used fsm1 as the name, I can collect the various states using sqlite3 query as follows:

pi@cms:~ $ sqlite3 -cmd “.timeout 5000” “file:/home/pi/pms.db?mode=ro”
SQLite version 3.16.2 2017-01-06 16:32:41
Enter “.help” for usage hints.
sqlite> .schema
CREATE TABLE pmsstatus(
id integer primary key autoincrement,
hostname text not null,
status text not null,
timestamp DATETIME);
sqlite> select * from pmsstatus;
1|fsm1|g|2017-12-18 15:57:28.571286
2|fsm1|g|2017-12-18 15:57:36.181199
3|fsm1|r|2017-12-18 15:57:41.759373
4|fsm1|y|2017-12-18 15:57:47.346998
5|fsm1|g|2017-12-18 15:57:50.909277
6|fsm1|r|2017-12-18 15:57:58.512628

This shows a set of rows with hostname as fsm1 and status shown in third column, and timestamp in fourth column. This data could be analyzed to pick out Response time to Service, and other useful information.

Note we have used mode=ro in this connection, so that the main application will use normal read/write mode and the web interface will use the read only mode.

 

 

Rooting Smartbox X96

13 Apr

I had used Kodi on Rspberry Pi and enjoyed the comfort of watching movies at home. So when I read about 4K video with H265 and the power of a Quad Core 2GB 16GB system using Amlogic S905X SoC I was tempted. The cost including shipment from Sztomato in China was coming to about $57. But I had one supplier available in India through ebay.in at similar prices. Since I was not sure about customs duty, I opted for local supplier. The box is fine and works well. I even pried open the base to see the board and was happy to see the solid Aluminium heat sink. The WiFi capability is good and I had un-interrupted viewing pleasure of high quality movies through my Internet Service Provider.

Soon, I wanted to connect to it from my laptop and do some developments. Simple things like Terminal and SSHDroid was easy. But when I wanted to become SuperSU, I had difficulty. Since I could not find a source to root the device, I called Sztomato. They said there are too many models of the same Amlogic S905X being sold by a number of companies. Yet when I requested them for a rooting file, they refused, and said their own products are sold with rooting enabled. I asked my supplier in India, if he could guide me to get a zip file that could be used for the rooting. He said “that will void the warranty”. I am sure he will give me no warranty even if something happened.

My intention was that I should have a backup of my system on a 32GB SD card. The thought of sending this device back to China for just reloading the software was a horrible idea. So here is what I did to do the rooting. Please note that the X96 has a small reset switch behind the IR hole. You can flip open the back cover easily and check that out or you can use a tooth pick head inside and feel the switch. If you power switch with the reset switch pressed and it will boot to the recovery screen. Form here you can install any special applications like TWRP recovery image. Don’t do it. Try by using the built in recovery and the rooting file I used. If this will work I suggest stay with the original recovery file. I did through TWRP. So I used a new recovery file and flashed that over the device. No major damage. Now when I press the rest and reboot it gets into TWRP recovery screen.

http://www.memoryleaks.org/libreelec.tv/twrp/twrp.s905x.img is the file I used. Save it to the root of an sd card and rename it as recovery.img. Then I used the built in recovery and flashed the recovery partition. Please not that this may not be needed, and try to avoid it first. I survived somehow. After this when you reboot the system will have TWRP as the recovery manager.

Next I installed SuperSU. This will not get you into Super User as the system does not have the bianries for “su” yet. So Now I downloaded SuperSU-v2.46.zip and installed it using the recovery “Install” option of TWRP. The built in recovery also has “Install” from zip file option. Please try that first.

Now I enabled “developer” option and started up SuperSU. Now this got me into su mode. Tested by getting into terminal and typing “su” and it gave me root option.

After this I took a backup of my system using Backup option in TWRP. (Note same should be available in original X96 recovery option.)

Next big effort is to get Android Studio working with my X96. I could connect using adb connect <ip address>. So now my Android Studio can access the device.

Cheers

 

 

Why skill gap for tech graduates?

20 Mar

The Kolkata Telegraph of March 20, 2017, had an article on “Skill gap trips tech graduates”. This is a common problem well recognised by IT industry in West Bengal. Even MTech in Engineering had to struggle to find an entry level job. The main reason is lack of skills.

A similar problem was identified a few years back in UK, where students coming to Cambridge were found to lack in “coding” skills. They found the solution by revamping the school curriculum and placing importance in “Computing” as a science subject and getting rid of the ICT boring subject. You can read more about it at:
https://www.theguardian.com/commentisfree/2012/apr/08/eric-schmidt-improve-computer-education&#8221; and the story of the birth of Raspberry Pi as an import tool for revitalising computing at school level. Thanks to industry support with BCS and Computing at School (CAS), the Raspberry Pi has now become the foundation of Computing studies in schools. Many teachers have been trained in UK to use the Raspberry Pi.

Why this miracle cannot happen in West Bengal or India? Why are we just talking of the problem for the past few years without implementing the remedy at school level? May be it is time to get the parents involved. We all know, that we cannot easily change the CBSE and ICSE syllabus. We cannot change the passion of rich schools to buy Desktop Windows computers and teach students Microsoft Word and Power Point. So here is a challenge for you parents. It does not cost much! In fact it is an ideal gift for the child’s birthday. It will give your child an introduction to using Raspbian and learn Python.

Gift your child a Raspberry Pi. https://www.raspberrypi.org/learning/teachers-guide/ has enough guides on how to get it working. Python is the language that has strong Mathematical foundation and is ideal for school students. Here is a link on how to get it going on the Pi: https://www.raspberrypi.org/documentation/usage/python/ .

The GPIO on the Raspberry PI is a great feature to bring out the “maker” spirit in children. You can easily build simple projects using LEDs and Relays. Magpi the magazine for Raspberry Pi has a large number of interesting projects that will challenge any child. If this foundation is created at school level, you don’t have to worry about the “Skill gap” talked about in IT industry.

I am always available for any parent who wishes to give hand holding to their child.

You can reach me : jpm.nina at gmail