Categories
Raspberry Pi

Microchip AR1100 Resistive Touch Screen to USB

I’m currently working on a project similar to what Rune Kyndal did, which is putting a Raspberry Pi inside an old HP 95LX case. You can see Rune’s project on HACKADAY.IO.

I sourced a 800×320 screen from BuyDisplay.com, that is a good match for the original dimensions of the HP 95LX display. Arguably, the screen that Rune sourced is better at 800×480, but I wanted a display that would allow for the original faceplate to be used so it looks as close to the original as possible. I still need to cram everything into the housing.

One thing that I wanted to be able to do is use the device without a mouse if I want to. That requires getting the touch screen to work. Today I was finally able to get the touch screen in a usable state with the help of tool created by tom-2015 and available on GitHub. The application runs on Linux, which I needed as the screen is at an odd resolution so the Microchip calibration tool will not work for the display. I was able to get Windows to use the display, when connected through HDMI, but was unable to get the correct resolution for calibration.

The rpi-AR1100 application provided on GitHub does the trick but I did have an issue that I needed to work through first. It took a few days of looking through the Microchip datasheet and walking through the code to determine that it was a timeout issue with reading/recognizing the calibration points being touched.

Issue Prior to Calibration

Before Calibrating the screen, the touch did not track to the edges of the screen. It was fine in the middle of the screen but as you got out to the edges, the pointer was not tracking and would not reach to the edges of the screen. Below is an image with a red rectangle showing the approximate bounds of the touch area prior to calibration.

Rectangle showing perimeter of the pointer boundary prior to calibration

The AR1100 works well once it is calibrated. If you calibrate it on the Raspberry Pi using Tom’s rpi-AR1100 utility on GitHub and find that the calibration is not responding to touches, you may need to edit line 185 in AR1100.cpp by changing the timeout parameter passed to the read_data function. I made the following edit to get it working on the Raspberry Pi Zero W.

Changed AR1100.cpp line 185
FROM: int result = read_data((unsigned char *) & response, sizeof(response), & bytes_read, 5);
TO: int result = read_data((unsigned char *) & response, sizeof(response), & bytes_read, 50);

UPDATE: Tom has made this change to the code in his repository. If you find that the application is not responding when touching the screen, you may need to increase this value.

Hope this helps you if you are working with the Microchip AR1100.
BTW: I’m using the Adafruit Resistive Touch Screen to USB Mouse Controller – AR1100, product number 1580.

Categories
Raspberry Pi

Raspberry Pi 4 – 19″ Rack with four units

Source Files are located on GitHub at https://github.com/richteel/pi_status.

The inspiration for this project is from a UCTRONICS Pi Rack that Jeff Geerling reviewed on his YouTube channel. The UTRONICS product may be seen at https://www.uctronics.com/cluster-and-rack-mount/for-raspberry-pi/1u-rack-mount/uctronics-pi-rack-pro-for-raspberry-pi-4b-19-1u-rack-mount-support-for-4-2-5-ssds.html. While I cannot build something as elegant as the UCTRONICS Pi Rack, I wanted to do something similar. I had a 1U ABS 19″ Rack Mount Case, so I decided to replicate the UCTRONICS product. Below is some information on my build.

Materials

Notes

  • Source and product pages are representative of items. Materials may be sourced from other vendors such as Adafruit, Digi-Key, Mouser, etc.
  • Currently (October 2022), Raspberry Pi boards are difficult to source. The situation should improve shortly. It is recommended that you use Raspberry Pi boards that you currently have or wait until they are available at approved vendors such as PiShop.us, Adafruit, Digi-Key, SparkFun, etc. You may be able to get a Raspberry Pi 4 at Amazon but it will be nearly triple the retail price. Below is the Amazon listing for a Raspberry Pi 4 with 4GB of RAM at $149.90 verses the retail price of $55 US.
Raspberry Pi listing on Amazon

You may find official retailers with Raspberry Pi boards in stock by going to https://rpilocator.com/.

Tools and Prerequisites

  • Laser Cutter or hand tools for cutting mounting and front plates
  • Ethernet Switch/Hub with PoE capability if using PoE+ Hat. If you plan to use PoE, your switch must support the IEEE 802.3at standard. (See Jeff Geerling’s video Review: Raspberry Pi’s new PoE+ HAT)

Laser Cutting Files

Download the files from GitHub at https://github.com/richteel/pi_status.

There are two files located in the case folder for use with a laser cutter. The front panel will be having some changes shortly so be warned if you decide to use it that it will not fit properly. I had only 1/8″ material so it is smaller to fit the case for testing. I now have 1/16″ stock and will be modifying the file for the 1/16″ material to fit properly in the case. I will also modify the notch for the USB to SATA adapter cable. I’m considering running them on top of the mounting plate rather than under it as it is not a good fit. If I do that, I may move the SD Card slot back to the center to avoid running it over a mounting hole.

The base was made with 1/8″ draftboard. The outline for the Raspberry Pi’s were scored while the rest was cut.

Glowforge Application – Mounting Plate
Glowforge Application – Front Panel

Assembly

The following directions utilize an unsupported method for mounting the PoE Hat. The PoE Hat should be mounted with the provided 10mm standoffs to provide a solid connection to the Raspberry Pi. Using the 11mm standoffs reduces the mating area but provides more contact area to allow a male header to make connection with the GPIO pins through the top of the PoE Hat. If you have a 2U case, it would be best to utilize the recommended 4 & 40 Pin Extra Tall Header (Push Fit Version) – POE HAT Set and a perfboard to make the GPIO connections. I attempted this setup but was unable to make it fit in the 1U case. That explains why UCTRONICS uses pogo pins on the bottom of the Raspberry Pi to make the GPIO Connections.

  1. Once the mounting and front plates are cut, the assembly may begin. The M2.5*5+5mm Male-Female Standoffs are secured to the mounting plate with M2.5 nuts for the Raspberry Pi mounts.
  2. The plastic housings for the Micro SD to Micro SD Card Extension Cables are removed and are secured to the mounting plate with the double sided tape provided. Make certain to line them up with the front plate and make certain that they are flush or extend slightly from the front plate.
  3. Once the standoffs and Micro SD Card Extension Cables are mounted, secure the mounting plate to the case with the 6#32*6BM or similar screws. You will not be able to put a screw under the second Raspberry Pi from the right as the Micro SD Extension Cable passes over the mounting point. I did not place any screws under either Raspberry Pi.
  4. Secure the Raspberry Pi boards to the mounting plate with the M2.5*11+6mm Male-Female Standoffs.
  5. Plug the Raspberry Pi PoE+ HATs into each Raspberry Pi and secure with the M2.5*5mm Screws.
  6. Secure the M2*4+3mm Male-Female Standoffs to the front plate with the M2*4mm Screws for mounting the displays.
  7. It is recommended to use MX1.25-4P Male & Female connectors to connect the switches to the 2×7 Header as the switches cannot be removed easily if not used. If you will not use the connectors, mount the switches to the front panel.
    Solder wires to the push-button switches.
  8. Solder wires to the OLED displays.
  9. Solder the wires to the 2×7 2.54mm Double Row Male Header. Refer to the diagram below. It is recommended to use a perfboard or heat-shrink to prevent accidental shorts.
  10. Once all the wires are soldered, mount the displays on the front panel with the M2 Nuts.
  11. Mount the push-button switches and plug the switches into the connectors if the connectors were used.
  12. Mount the front panel into the case.
  13. Spread the 2×7 male header pins apart slightly to provide a better connection when inserting through the top of the PoE Hat.
  14. Carefully plug the 2×7 male header into the PoE Hat. Make certain that you pay attention to the orientation. It is easier if you place the inside row into the PoE Hat then work the outside row into the PoE Hat.
  15. Double check that all connections are correct and all the screws, nuts, and standoffs are tight.
  16. Do not close the case yet! You need to test each Raspberry Pi first as it is likely that one or more of the pins on the 2×7 header will not be making contact with the GPIO pins so you will need to spread some pins apart a bit more to make good contact. It is also possible that one or more PoE hats will not make good contact with the Pi so that the fan will not run when the Raspberry Pi gets hot. You will need to check that everything on all Raspberry Pi boards are working before closing up the case.
  17. Once everything is working, you may close the case.
Wiring Pinout for the Front Panel and Button

Below are a few photos of the build.

Header Pins Spread Apart
Showing wiring using a perfboard for the 2×7 male header.
Mounting Plate with one Raspberry Pi mounted. (The screw under the 3rd Raspberry Pi was not installed in the final build.)
Installation showing SSD Drives installed.

Software Installation

Follow the steps in the README.md file in the GitHub repository to install the pi_status software on each Raspberry Pi.

Testing and Troubleshooting

No Power to the Raspberry Pi

  • Check if the PoE Hat has power.
    • Is the Red LED lit on the PoE Hat? – No
      • Does the network switch provide PoE+ (IEEE 802.3at standard)?
      • Is the switch port that the Raspberry Pi is connected to provide PoE+? (Some switches supply PoE+ to some but not all ports.)
      • Try another ethernet cable to see if that is the problem. Make certain that there are 4 pairs in the cable as all 8 wires are needed for PoE.
    • Is the Red LED lit on the PoE Hat? – Yes
      • Attempt to reseat the PoE Hat.
      • Unplug the 2×7 male header to see if there is a fault causing a short.

Raspberry Pi boots but the fan does not run when the Raspberry Pi is over 50C

  • Attempt to reseat the PoE Hat.
  • Unplug the 2×7 male header to see if there is a fault causing the issue.

Display does not work at all

  • From a Terminal Window, connect to the Raspberry Pi and run i2cdetect to see if device 3c is found.
    i2cdetect -y 1
  • Device 3c is found – No
    • Remove the 2×7 male header and spread apart the pins a bit more or if not all pins are spread apart the same, spread the pins that are closer together apart further until they are all spread apart the same distance. Most likely the issue will be with pins 1 through 6 so concentrate your effort on those pins.
    • Check for cold/bad solder connections on the display lines.
  • Device 3c is found – Yes
    • Make certain that the script is running. Run the following command to get the pid of the running script. If no pid is returned, then the script is not running.
      ps ax | grep ‘code.py’ | grep -v grep
    • If the script is not running, try to run it again.
      sh /home/pi/pi_status/display/launcher.sh -v
    • Check the error log to see if any errors were logged.
      cat /home/pi/pi_status/display/logs/error.txt
      or
      sudo tail -f /home/pi/pi_status/display/logs/error.txt

Display is garbled or stops working

  • Most likely, the display wires are too close to the transformer on the PoE Hat. Move the display wires so they are not close to the transformer and restart the script.
  • Double check that Display SDA is connected to the Raspberry Pi SDA and the Display SCL is connected to the Raspberry Pi SCL.
  • Check for cold/bad solder connections on the display lines.

LED on the switch is not lit and/or switch does not shutdown the Raspberry Pi

  • Remove the 2×7 male header and spread apart the pins a bit more or if not all pins are spread apart the same, spread the pins that are closer together apart further until they are all spread apart the same distance. Most likely the issue will be with pins 7 through 14 so concentrate your effort on those pins.
  • Check for cold/bad solder connections on the switch wires.
  • Make certain that the switch and LED are connected to the correct GPIO pins on the Raspberry Pi.

Going Further

Once everything is working as expected, you can set up the Raspberry Pi boards to boot from SSD instead of the Micro SD Card. Below is a list of high-level steps that I followed to boot from the SSD Drives.

Steps from https://www.tomshardware.com/reviews/raspberry-pi-headless-setup-how-to,6028.html to setup a headless Raspberry Pi.

Raspberry Pi Imager

  • Choose OS button
    • Select your OS (Pi 4)
      • Raspberry Pi OS (other)
        • Raspberry Pi OS (64-bit)
  • Choose Storage
  • Click the settings button or hit CTRL + Shift + X to bring up the settings menu
    • Set Hostname
    • Enable SSH
    • Set username and password
    • Set Wi-Fi network SSID, password, & country
    • Set Locale
    • Click Save
  • Choose Write

Enabling I2C & VNC

  • Open SSH Session
  • sudo raspi-config
    • Select Interfacing Options
      • Select I2C
        • Turn on the option
    • Select Interfacing Options
      • Select VNC
        • Turn on the option, then exit

Steps from https://github.com/richteel/pi_status to setup the display and button.

Setup Display and Button

  • Open SSH Session
    • sudo apt-get update
    • sudo apt-get upgrade
      * May want to reboot if updates were applied that require reboot
    • sudo pip3 install adafruit-blinka
    • sudo pip3 install adafruit-circuitpython-ssd1306
    • git clone https://github.com/richteel/pi_status.git
    • sudo crontab -e
    • Add the following line to the end of the file
      @reboot sh /home/pi/pi_status/display/launcher.sh
    • Reboot to make certain that the script starts at boot
      • sudo reboot now

Steps from https://www.tomshardware.com/how-to/boot-raspberry-pi-4-usb to boot from USB.

Allow booting from USB

  • Boot Raspberry Pi from SD Card
  • Open VNC to machine and use the SD Card Copier to copy SD Card to USB Drive
  • Open SSH Session
  • sudo raspi-config
    • Select Advanced Options
      • Select Boot Order
        • Select USB Boot
  • Shutdown the Raspberry Pi
    • sudo shutdown now
  • Remove the SD Card and restart the Raspberry Pi
Categories
Psion Raspberry Pi Pico

Re-creation of Psion Organiser II

In 1984, Psion launched the first practical pocket computer, the Psion Organiser I. Psion followed up with the Psion Organiser II.

Andrew Menadue has a project which recreates the Psion Organiser II with the Raspberry Pi Pico and ESP32-WROOM-32. He has several videos on YouTube and some GitHub repositories. Andrew was nice enough to send me a set of boards, which I’m in the process of building.

Bill of Materials

The bill of materials is an Excel document located on GitHub.

Power Supply

Categories
Raspberry Pi Pico

Armachat

I have been working on the Armachat project by Peter Misenko. I rewrote the code to implement functions that Peter had not completed and added a few more. Some of the added features include:

  • Implemented detection of a long keypress
  • Dedicated keys when not in the editor
    • Q – Toggle the keyboard backlight
    • A – Toggle the display backlight
    • V – Increase volume / Long keypress to decrease volume
    • B – Increase display brightness / Long keypress to decrease display brightness
  • Ability to receive messages in all screens including editor
  • Validation of editor input
  • Confirmation prompts

I have been working on an issue with editor text input. The display update procedure takes 400 to 500 milliseconds which causes an issue as keypresses are dropped when typing quickly. Initially it was taking nearly 1 second to update the screen so some improvement has been made but it is still not good enough. I continue to work on improving the screen draw time and may deploy a few things to allow all keys to be detected and handled.

Another area for improvement is the long keypress detection. I noticed that removing the long keypress detection speed up the code enough to catch all key presses however the added functionality that the long keypress provides is too much to ignore.

GitHub locations for Peter’s code and my rewrite.

Categories
Raspberry Pi

SainSmart 2.8″ TFT LCD Display Touch Screen on Raspberry Pi

I have a SainSmart 2.8″ TFT LCD Display Touch Screen a couple of years ago from MicroCenter. I want to use it on the Raspberry Pi and was surprised that no one has posted how to do this. I looked into this a bit and it looks like it should be easy enough. One of the things that throws folks off is the display is 16 bit verses 8 bit that is typically used on these displays. You can still use them with only 8 bits of data. Unfortunately SainSmart does not provide information on their product pages to allow their products to be used with anything other that their adapters or other products.

Here are some useful links that will be used in this writeup.

  1. Product Page for SainSmart 2.8″ TFT LCD Display Touch Screen
  2. Discussion page on Raspberry Pi Forum
  3. Zip file mentioned in the Raspberry Pi Forum post by M15H
  4. Posts on Adafruit’s Leaning site
LCD Raspberry Pi
Pin Function Pin Function
1 GND 6, 9, 14, 20, 25, 30, 34, 39 GND
2 VCC 1, 17 3V3
3 NC
4 RS 12 GPIO 18
5 WR 11 GPIO 17
6 RD  1, 17 3V3
7 DB8 15 GPIO 22
8 DB9 16 GPIO 23
9 DB10 18 GPIO 24
10 DB11 19 GPIO 10
11 DB12 22 GPIO 25
12 DB13 21 GPIO 9
13 DB14 23 GPIO 11
14 DB15 24 GPIO 8
15 CS 7 GPIO 4
16 F_CS
17 RESET 26 GPIO 7
18 NC
19 LED-A 1, 17 3V3
20 NC
21 DB0 6, 9, 14, 20, 25, 30, 34, 39 GND
22 DB1 6, 9, 14, 20, 25, 30, 34, 39 GND
23 DB2 6, 9, 14, 20, 25, 30, 34, 39 GND
24 DB3 6, 9, 14, 20, 25, 30, 34, 39 GND
25 DB4 6, 9, 14, 20, 25, 30, 34, 39 GND
26 DB5 6, 9, 14, 20, 25, 30, 34, 39 GND
27 DB6 6, 9, 14, 20, 25, 30, 34, 39 GND
28 DB7 6, 9, 14, 20, 25, 30, 34, 39 GND
29 D_CLK -23-  -GPIO 11-
30 D_CS -26- -GPIO 7-
31 D_DIN -19- -GPIO 10-
32 D_BUSY
33 D_OUT -21- -GPIO 9-
34 D_Penirq
35 SD_OUT(F_SI)
36 SD_SCLK(F_SCK)
37 SD_DIN(F_SO)
38 SD_CS
39 F_WP
40 F_HOLD

Raspberry Pi Pinouts
Raspberry Pi Pinouts

 

ILI9325

http://www.anya.bagge.no/hacks/2018/02/17/tft-panel/

https://www.raspberry-pi-geek.com/Archive/2014/07/TFT-touchscreens-for-the-Raspberry-Pi

https://www.raspberrypi.org/forums/viewtopic.php?t=208264

 

 

Categories
Raspberry Pi

Pipboy 3000

OVERVIEW

My daughter wanted to cosplay as a character from Fallout 3 for Awesomecon 2018 in Washington D.C. We found Noe and Pedro Ruiz’s design on learn.adafruit.com 1 and decided to build it. She decided do this cosplay a few days before the event so it was going to be a challenge to complete it in time but we did it.

I thought getting the electronics in time was going to be the biggest challenge but the biggest challenge was reducing the time for the 3D prints. We started on Sunday and did not finish until Wednesday afternoon. (We did not print continuously so the overall print time was less but not by much.) Noe and Pedro have some tips on reducing print time and they worked in combination with tweaking the infill and layer height.

3D PRINTING

My 3D printer is the XYZprinting daVinci 1.0. The printer is a few years old now and is not the best 3D printer by far but it does a fair job. The printer only supports ABS fulfillment and XYZprinting filament cartridges. I have yet to take time to change the head to support PLA or hack the firmware so I can use other filaments. This limits me to the colors of filament that I may use in the printer. We looked for a brown or green filament to use for the Pipboy but did not find any to my daughter’s liking. We decided to print it in white and spray paint it. We found some metallic paint which seemed to work well. Additionally, we printed the screen visor in black and the LED diffuser in natural.

As a side note, I started using Simplify3D Slicing Software with the printer and it has made a world of difference with the print quality and control of the printer. The prints come out much closer to the designed measurements than the XYZprinting software could accomplish. The software gives more options than the XYZprinting software which really came in handy for this project. Noe and Pedro recommend removing some of the supports for the armband and base-main pieces. The XYZprinting software is all or nothing regarding supports but the Simplify3D software allowed me to remove some supports while leaving others in place.

HARDWARE

Noe and Pedro do a good job in the write-up but as with most build instructions, a few things are left out or are not clear. Here are a few notes that I made along the way but it is not a complete list as we were under a very restrict time constraint to get this project done. Unfortunately that means take notes and documenting issues was not a priority.

Additional Materials needed but not listed in the materials list in the overview.

  • Electronic Parts & Components
  • Tools & Supplies
    • (Optional) Hot Glue and Hot Glue Gun
    • Adhesive foam such as weatherstripping for doors and windows

Below are the connections to the 3.5″ LCD Shield that were used in our build.

PypBoy Function Pin Pin Function PypBoy
LED + 3V3 1 2 5V  
ENC A GPIO2 3 4 5V PB 1000C +
ENC B GPIO3 5 6 GND PB 1000C –
ROT 1 GPIO4 7 8 GPIO14 ENC SW
LED – GND 9 10 GPIO15  
ROT 2 GPIO17 11 12 GPIO18  
ROT 3 GPIO27 13 14 GND ROT –
ROT 4 GPIO22 15 16 GPIO23 ROT 5
  3V3 17 18 GPIO24  
  GPIO10 19 20 GND  
  GPIO9 21 22 GPIO25  
  GPIO11 23 24 GPIO8  
ENC – GND 25 26 GPIO7  

SOFTWARE

We made a few tweaks to the software but they were to change the map and display some different text on some screens and adding some missing code to change other displayed text. I plan to post those minor changes in a future post. The more major changes implemented were to get the pypboy program to launch on start and wire up the rotary encoder so we can safely shutdown the Raspberry Pi.

The shutdown code was modified from Inderpreet Singh’s code on Element14’s website2.

REFERENCES

  1. Noe and Pedro’s writeup on learn.adafruit.com: https://learn.adafruit.com/raspberry-pi-pipboy-3000
  2. Inderpreet Singh’s Shutdown Code on Element14’s site: https://www.element14.com/community/docs/DOC-78055/l/adding-a-shutdown-button-to-the-raspberry-pi-b
Categories
Raspberry Pi

Today’s Tricorder Project Update

Ticorder

Progress has been slow on revision 2 of the TOS Tricorder. I have been working on the software, particularly the plugins for the sensors. I am currently working on the I2C sensors. I have 2 of the 8 sensors coded but found an issue with detecting the I2C devices in the init method without getting the I2C bus into a stuck state. I finally figured out this issue this morning and updated the Raspberry Pi forum post were I found the original code that I used. If you are interested, you may check out the post at https://www.raspberrypi.org/forums/viewtopic.php?f=32&t=114401.

Another quick note, I found the lambda function in Python. I may put this to some good use. I had to do something similar but found a way around it as I never stumbled on this gem. In many ways, I wish I stuck to C++ for this project but it is good to get to know yet another language.
References:
https://stackoverflow.com/questions/30325351/ioerror-errno-5-input-output-error-while-using-smbus-for-analog-reading-thr
http://www.secnetix.de/olli/Python/lambda_functions.hawk

GitLab Source: https://gitlab.com/richteel/TOSTricorder/tree/v2.0.0.0

Categories
Arduino Raspberry Pi

I2C Communications between Raspberry Pi and Arduino – Update

Over the past few days, I have been able to bit bang the I2C bus with the PIGPIO library. I found a very helpful example at http://raspberrypi.stackexchange.com/questions/3627/is-there-an-i2c-library/4052 which was posted by crj11. This example was using the pigpiod_if.h library. This worked well but required the application to be run with elevated privileges (sudo) which was not acceptable as I needed to run the application from a Python script which in turn was running every 5 minutes from a cron job. The final solution was to use the pigpiod_if2.h and run the pigpio daemon on startup.

The final solution has been running for a few days with three sensors connected over a total of 9 feet (~3 meters) of cable. The data has been logged at Adafruit.IO which I have made public. I may remove or rename the Dashboard in the near future so here is a screenshot of the page.
adafruit_io_v0.1.0

I have also updated the GitHub site for the project so there are now two releases available. Version RC_0.0.1 is the version using the hardware for I2C control and version RC_0.1.0 is the bit bang version.

I still have more work to do to make this project valuable to others. I plan to create some better documentation on the project and provide a full write-up to allow someone to follow along and build there own from start to finish. Right now, 80% to 90% is captured in various places but there are obvious gaps such as the connection to the Raspberry Pi. One can figure this missing information out by looking through the right source files and piece it together however I do not like it when I find a project write-up that is only 80% to 90% documented. It is still better than nothing or only 10% to 25% though.

I hope someone will find some of the information here useful.

Categories
Arduino Raspberry Pi

I2C Communications between Raspberry Pi and Arduino – Update, BOM, and Source Control

Update

I have been working on this project over the past couple of weeks when I have free time but have not been posting updates. This is a general update which is why the title is different from the other posts regarding this project.

My boards from OSH Park arrived last week. I was able to populate them and test them out. Fortunately I did not make any errors on the PCB or schematic so they all worked as designed. There are a few things that I would change on a future version if I choose to make another version of the board.

  1. I would put the ICSP header on the bottom of the board so it does not stick out from the front. This would make it much easier to assemble and would make it possible to not have any exposed circuitry which may allow the device to be damaged from static electricity.
  2. I would move the resistors toward the bottom of the board if possible. It would allow the DHT11 sensor to stick out further from the case.
  3. I would also try to push the ATtiny85 a little further towards the bottom for the same reason as the resistors.

Currently I am looking to bit bang the I2C bus on the Raspberry Pi. I seem to have gotten around the clock stretching issue if there is only one device connected to the I2C bus but as soon as I add another device, the clock stretching becomes an issue again. I really wish that the Pi Foundation would work with Broadcom and fix the issue with the I2C bus.

Here are some pictures.

This slideshow requires JavaScript.

Bill of Materials (BOM)

Materials List (For One Sensor)

  • QTY 1 – PCB (Source: OSH Park)
  • QTY 2 – 3.5mm 4 Pole Audio Jack, J1 & J2 (Source: MCM Electronics NOTE: I ordered 27-9485 but received ones which look like 27-9487)
  • QTY 1 – 2×3 Position Male Header 2.54mm Pitch, JP1 (Source: MCM Electronics NOTE: This is a 2×13 header. You will need to cut it for 2×3 header.)
  • QTY 1 – T-1 3mm LED, LED1 (Source: MCM Electronics NOTE: May choose another color)
  • QTY 1 – CDS Photoresistor Photoelectric 5549 GL5549, PH1 (Source: Ebay)
  • QTY 2 – Resistor 1/4W 10K Ohm, R1 & R2 (Source: MCM Electronics)
  • QTY 1 – Resistor 1/4W 220 Ohm, R3 (Source: MCM Electronics)
  • QTY 1 – DHT11 Digital Temperature & Humidity Sensor Moudle, SE1 (Source: Ebay)
  • QTY 1 – ATMEL ATtiny85, U1 (Source: Newark)

Materials List for Raspberry Pi Hat

  • Qty 1 – Perfboard (Source: Adafruit)
  • Qty 1 – GPIO Header for Raspberry Pi NOTE: The one you will need depends on which model of Raspberry Pi you are using. (Source: Adafruit 2×13 Header for original Raspberry Pi or 2×20 Header for newer Raspberry Pi)
  • QTY 1 – 3.5mm 4 Pole Audio Jack (Source: MCM Electronics NOTE: I ordered 27-9485 but received ones which look like 27-9487)
  • Optional items

Source Control

I have added the source files for the Hardware and Software onto GitHub. I did this so the community may have access to the files and any updates to them. I mainly did it because I was having a hard time remembering which set of files I last worked with especially if a few days went by when I could not work on the project. I think this is a win-win for me and anyone interested in this project.

The files are located at https://github.com/TeelSys/TeelSys_THL. When you first go to the page, it may look like there are mo project files included in the project. If that is the case it is because I am still trying to get everything working properly before I commit code to the master branch. You will see a button with the text “Branch: master” and a downward arrow. Click that button and select another branch such as “dev”. You will then see the project files in their current state.

If you wish to contribute, add a comment here or if you can request through GitHub, do that. I will reply once I see the request but keep in mind that it may be a few days.

Categories
Arduino Raspberry Pi

I2C Communications between Raspberry Pi and Arduino – Part Four

It has been two weeks since my last post but it has been out of frustration on porting the code over to the ATtiny85. The first thing that I ran into was that the Wire library is not supported on the ATtiny85. I needed to modify my code to work with the TinyWireS library. This did not seem too bad and worked once in a while. It was a bit frustrating as I followed examples and it appeared that I was doing everything correctly but that is typically how it goes when coding.

I finally took a look at the specs for the ATtiny85 and realized that memory may be my issue so I started to pare down the memory requirements. The Arduino IDE was not complaining but I recalled an posting that was published on Adafruit a couple of years ago called Arduino Memories. After rereading the article and looking at a couple of other references, I determined that I needed to tackle the memory is see if it was an issue.

At some point in my debugging, I had noticed that the examples for TinyWireS were utilizing a buffer and pointer method to do fast reads and writes. I had a significant switch statement on the request data handler so I removed that and went with the buffer option. By doing so I reserved a whopping 256 bytes for the buffer. This was a very stupid move which I realized when I took a look at the specs for the the ATtiny85. The ATtiny85 has only 512 bytes of RAM so I was consuming half of it for the buffer which did not leave much room for anything else.

I dropped the buffer size down to 32 bytes which helped a great deal. After reducing the size of the buffer, I could get communications between the ATtiny85 and the Raspberry Pi to work a few times before the communications stopped working. I further refined the code to reduce memory usage and swapped out the Adafruit DHT library for one written by Rob Tillaart for the DHT11 only.

Book1

With these modifications, I was able to get the code down to using 113 bytes of RAM and 4,918 bytes (60%) of Flash.

With these changes, the code works quite well but sometimes it appears that the ATtiny85 does not read the correct request from the Raspberry Pi. After some searching it was found that there is a known issue with the Raspberry Pi and clock stretching. It appears that there is a bug which has not been fixed yet if the slave stretches the clock at the right moment and the stretching is too short. The ATtiny85 implements I2C in software so this is going to happen at some point.One of the best articles on this issue is the Raspberry Pi I2C clock-stretching bug.

There are some suggested fixes which I need to read more to understand well enough to use. The most promising fix appears to use Python to perform I2C communication in software. The recommendation is to use the PiGPIO library.

Below is the code that I have thus far on the ATtiny85.

// Uses DHT from Rob instead of Adafruit
// http://playground.arduino.cc/Main/DHTLib
// http://playground.arduino.cc/Main/DHT11Lib


#include <TinyWireS.h>
#include <dht11.h>

#define SLAVE_ADDRESS 0x23

#define PIN_DHT 4
#define PIN_PHOTORESISTOR A3
#define PIN_LED 1

unsigned long previousMillis = 0;
#define interval 2500

#define bufferSz 32
byte dataBuffer[bufferSz] = { 32 };
uint8_t bufferIdx = 0;
boolean firstByteRead = false;

dht11 DHT11;

// Union used to convert float to byte array
union u_tag {
  byte b[4];
  float fval;
} fdata;

void setup() {
  pinMode(PIN_DHT, INPUT);
  pinMode(PIN_PHOTORESISTOR, INPUT);
  pinMode(PIN_LED, OUTPUT);

  digitalWrite(PIN_LED, HIGH);

  // initialize i2c as slave
  TinyWireS.begin(SLAVE_ADDRESS);

  // define callbacks for i2c communication
  TinyWireS.onReceive(receiveData);
  TinyWireS.onRequest(sendData);

  // Initialize dataBuffer
  for (int i = 0; i < bufferSz; i++) {
    dataBuffer[i] = 0xFF;
  }
  // Set LED to blink on each loop
  dataBuffer[3] = 2;
  // Load Model Info
  // T  S  0  0  0  0  0  1
  // 54 53 30 30 30 30 30 31
  String storeText = F("TS000001");
  bufferIdx = 0x10;
  for (int i = 0; i < storeText.length(); i++) {
    dataBuffer[bufferIdx] = storeText[i];
    bufferIdx++;
  }
  // Load Version Info
  // 0  0  0  0  0  0  0  3
  // 30 30 30 30 30 30 30 33
  storeText = F("00000003");
  bufferIdx = 0x18;
  for (int i = 0; i < storeText.length(); i++) {
    dataBuffer[bufferIdx] = storeText[i];
    bufferIdx++;
  }
}

void loop() {
  TinyWireS_stop_check();
  
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    if (dataBuffer[3] == 2) {
      digitalWrite(PIN_LED, !digitalRead(PIN_LED));
    }

    ReadDHT();
    ReadLightLevel();
  }
}

// callback for received data
void receiveData(uint8_t byteCount) {
  if (byteCount != 1)
  {
    // Sanity-check
    return;
  }

  while (TinyWireS.available()) {
    bufferIdx = TinyWireS.receive();
    firstByteRead = false;

    SetLedStatus();
  }
}

// callback for sending data
void sendData() {
  if (firstByteRead) {
    bufferIdx++;
  }

  firstByteRead = true;

  if(bufferIdx < 0 || bufferIdx >= bufferSz) {
    TinyWireS.send(0xFF);
    return;
  }

  TinyWireS.send(dataBuffer[bufferIdx]);
}

void ReadDHT() {
  int chk = DHT11.read(PIN_DHT);

  if(!chk==DHTLIB_OK) {
    return;
  }
  
  float humidity = (float)DHT11.humidity;
  float temperatureCelsius = (float)DHT11.temperature;
  
  SaveFloatToBuffer(0x04, temperatureCelsius);
  SaveFloatToBuffer(0x08, humidity);
}

void ReadLightLevel() {
  int photocellReading = analogRead(PIN_PHOTORESISTOR);
  float lightReading = ((float)photocellReading / 1023.0) * 100.0;
  SaveFloatToBuffer(0x0C, lightReading);
}

void SaveFloatToBuffer(uint8_t bufIdx, float val) { 
  dataBuffer[bufIdx] = 0;
  dataBuffer[bufIdx + 1] = 0;
  dataBuffer[bufIdx + 2] = 0;
  dataBuffer[bufIdx + 3] = 0;
  
  fdata.fval = val;

  dataBuffer[bufIdx] = fdata.b[3];
  dataBuffer[bufIdx + 1] = fdata.b[2];
  dataBuffer[bufIdx + 2] = fdata.b[1];
  dataBuffer[bufIdx + 3] = fdata.b[0];
  
  //dataBuffer[bufIdx] = (int)val;
}

void SetLedStatus() {
  if (bufferIdx > 2)
    return;

  dataBuffer[3] = 2;
  if (bufferIdx < 2) {
    digitalWrite(PIN_LED, bufferIdx);
    dataBuffer[3] = 0;
    if (digitalRead(PIN_LED) == HIGH) {
      dataBuffer[3] = 1;
    }
  }
}

Here is the code on the Raspberry Pi to verify that things are working.

#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>

#define CMD_GET_TEMPERATURE 1
#define CMD_GET_HUMIDITY 2
#define CMD_SET_LIGHT 3
#define CMD_SET_LED_ON 4
#define CMD_SET_LED_OFF 5
#define CMD_SET_LED_FLASH 6
#define CMD_GET_MODEL 250
#define CMD_GET_VERSION 251
#define CMD_GET_HELLO_WORLD 254

// The PiWeather board i2c address
#define ADDRESS 0x23

// The I2C bus: This is for V2 pi's. For V1 Model B you need i2c-0
char *devName = "/dev/i2c-0";
int file;
int devices[128];
int sensorDevices[128];

union u_tag {
	char b[4];
	float fval;
} fdata;

float computeHeatIndex(float temperature, float percentHumidity, int isFahrenheit);
float convertCtoF(float c);
float convertFtoC(float f);
void displayConnectedI2cDevices();
void dumpDeviceInfo(int deviceAddress);
void findAllI2cDevices();
void findI2cBus();
void findSensors();
float receiveFloat();
int receiveInt();
void receiveString(char *str, int bufSize);
int sendCommand(int deviceAddress, int cmdCode);

int main(int argc, char** argv) {
	// Look for the I2C bus device

  printf("I2C: Connecting\n");
	findI2cBus();
  
  // Find Devices
  findAllI2cDevices();
  
  // Display devices found (Simlar to i2cdetect -y 0)
  displayConnectedI2cDevices();
  
  dumpDeviceInfo(0x23);
  
  sendCommand(0x23, 0x04);
  float temperature = receiveFloat();
  sendCommand(0x23, 0x08);
  float percentHumidity = receiveFloat();
  sendCommand(0x23, 0x0C);
  float lightLevel = receiveFloat();
  
  printf("Temperature = %1.2f (C)\n", temperature);
  printf("Humidity = %1.2f\n", percentHumidity);
  printf("Light Level = %1.2f\n", lightLevel);

  close(file);
  return (EXIT_SUCCESS);
}

float computeHeatIndex(float temperature, float percentHumidity, int isFahrenheit) {
  // Using both Rothfusz and Steadman's equations
  // http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
  float hi;

  if (isFahrenheit==0)
    temperature = convertCtoF(temperature);

  hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094));

  if (hi > 79) {
    hi = -42.379 +
             2.04901523 * temperature +
            10.14333127 * percentHumidity +
            -0.22475541 * temperature*percentHumidity +
            -0.00683783 * pow(temperature, 2) +
            -0.05481717 * pow(percentHumidity, 2) +
             0.00122874 * pow(temperature, 2) * percentHumidity +
             0.00085282 * temperature*pow(percentHumidity, 2) +
            -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2);

    if((percentHumidity < 13) && (temperature >= 80.0) && (temperature <= 112.0))
      hi -= ((13.0 - percentHumidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);

    else if((percentHumidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0))
      hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2);
  }

  return isFahrenheit ? hi : convertFtoC(hi);
}

float convertCtoF(float c) {
  return c * (9.0/5.0) + 32;
}

float convertFtoC(float f) {
  return (f - 32) * (5.0/9.0);
}

void displayConnectedI2cDevices() {
	int idx=0;
	printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f");
	for(idx=0; idx<=0x7F; idx++) {
		if(idx%16==0) {
			printf("\n%d0:",idx/16);
		}
		if(idx>0x07 && idx<0x78) {
			if(devices[idx]>0) {
				if(devices[idx]==-9) {
					printf(" UU");
				}
				else {
					printf(" %02x", idx);
				}
			}
			else {
				printf(" --");
			}
		}
		else {
				printf("   ");
		}
  }
  printf("\n");
}

void dumpDeviceInfo(int deviceAddress) {
	int i=0;
	
	sendCommand(deviceAddress, 0x03);
	
	for(i=0x03; i<0x20; i++) {
		int val = receiveInt();
		
		printf("0x%02x: 0x%02x (%d)\t%c\n", i, val, val, val);
	}
}

void findAllI2cDevices() {
	int idx=0;
  for(idx=0; idx<=0x7F; idx++) {
  	int device=0;
  	
  	if(idx>0x07 && idx<0x78) {
	  	if (ioctl(file, I2C_SLAVE, idx) < 0) {
	  		if(errno == EBUSY) {
	  			device = -9;
	  		}
	  		else {
		  		device = -1;
		  	}
	  	}
	  	else {
	  		char buf[1];
	  		if(read(file, buf, 1) == 1 && buf[0] >= 0) {
	  			device = idx;
	  		}
	  	}
  	}
  	
  	devices[idx] = device;
  }
}

void findI2cBus() {
	if ((file = open(devName, O_RDWR)) < 0) {
  	devName = "/dev/i2c-1";
  	if ((file = open(devName, O_RDWR)) < 0) {
	    fprintf(stderr, "I2C: Failed to access %d\n", devName);
	    exit(1);
	  }
  }
  
  printf("Found I2C bus at %s\n", devName);
}

void findSensors() {
	char *sensorType="TeelSys Data and Light Sensor";
	char buf[256];
	int idx=0;
	int sensorIdx=0;
	// sensorDevices
	// devices
	
	// Clear the sensorDevices array
	for(idx=0; idx<128; idx++) {
		sensorDevices[idx] = 0;
	}
	
  for(idx=0x08; idx<=0x78; idx++) {
  	int device=0;
  	
  	if(devices[idx]==idx) {
  		if(sendCommand(0x22, CMD_GET_MODEL)==1) {
  			int bufSize = sizeof(buf)/sizeof(buf[0]);
  			receiveString(buf, bufSize);
  			if(strlen(sensorType)==strlen(buf) && strcmp(sensorType, buf)==0) {
  				sensorDevices[sensorIdx]=devices[idx];
  				sensorIdx++;
  				printf("Found Sensor at: 0x%02x\n", devices[idx]);
  			}
  		}
  	}
  }
}

void receiveString(char *buf, int bufSize) {
  int charCount=0;
  
	if(read(file, buf, bufSize) == bufSize) {
		for(charCount=0; charCount<bufSize; charCount++) {
			int temp = (int) buf[charCount];
			
			if(temp==255) {
				buf[charCount]=0;
			}
		}
  }
}

int receiveChar() {
  char buf[1];
  char retVal = 0x00;
  
  if (read(file, buf, 1) == 1) {
  	retVal=buf[0];
  }
  
	usleep(10000);
  return retVal;
}

float receiveFloat() {	
	fdata.b[3] = 0;
	fdata.b[2] = 0;
	fdata.b[1] = 0;
	fdata.b[0] = 0;
	
	fdata.b[3] = receiveChar();
	fdata.b[2] = receiveChar();
	fdata.b[1] = receiveChar();
	fdata.b[0] = receiveChar();
	
	return fdata.fval;
	//return (float)fdata.b[3];
}

int receiveInt() {
  return (int)receiveChar();
}

int sendCommand(int deviceAddress, int cmdCode) {
	int retVal = 0;
	unsigned char cmd[16];
	cmd[0] = cmdCode;
	
	if (ioctl(file, I2C_SLAVE, deviceAddress) < 0) {
    fprintf(stderr, "I2C: Failed to acquire bus access/talk to slave 0x%x\n", deviceAddress);
    exit(1);
  }
  
  if (write(file, cmd, 1) == 1) {
  	// As we are not talking to direct hardware but a microcontroller we
    // need to wait a short while so that it can respond.
    //
    // 1ms seems to be enough but it depends on what workload it has
    usleep(10000);
    retVal = 1;
  }
  
  return retVal;
}

Running the Raspberry Pi program produces the following result.

pi@raspberrypi:~ $ ./testi2c07a
I2C: Connecting
Found I2C bus at /dev/i2c-0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- 23 -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
0x03: 0x02 (2)
0x04: 0x41 (65) A
0x05: 0xb8 (184)        ▒
0x06: 0x00 (0)
0x07: 0x00 (0)
0x08: 0x42 (66) B
0x09: 0x0c (12)

0x0a: 0x00 (0)
0x0b: 0x00 (0)
0x0c: 0x42 (66) B
0x0d: 0x2d (45) -
0x0e: 0xff (255)        ▒
0x0f: 0x80 (128)        ▒
0x10: 0x54 (84) T
0x11: 0x53 (83) S
0x12: 0x30 (48) 0
0x13: 0x30 (48) 0
0x14: 0x30 (48) 0
0x15: 0x30 (48) 0
0x16: 0x30 (48) 0
0x17: 0x31 (49) 1
0x18: 0x30 (48) 0
0x19: 0x30 (48) 0
0x1a: 0x30 (48) 0
0x1b: 0x30 (48) 0
0x1c: 0x30 (48) 0
0x1d: 0x30 (48) 0
0x1e: 0x30 (48) 0
0x1f: 0x33 (51) 3
Temperature = 23.00 (C)
Humidity = 35.00
Light Level = 43.50

 

Next step is to see if I can resolve the clock stretching issue and then connect to adafruit.io to post data. If it is not possible to address the clock stretching issue, it would be possible to identify when it occurs and reset the power to the I2C slave devices. I am trying to avoid that solution but I may need to resort to that solution.