spi write up to 64 colors at a time on Arduino!

amg8833 equivilent sensor thermal cam is a perfect example of how to write faster with spi lcd displays. 128x128 sample fast on Arduino.

Similar projects worth following Spi writes are far from fast and are not properly optimized! This project allows data to travel at highway speeds for fast lcd updates even on Arduino spi!Imagine a highway using traffic stop signs, traffic would get backed up because of the time it takes cars and busses and trucks to get up to speed. spi protocol is busy. it is complex, and this causes traffic to back up a lot. one way to make it work better is to remove the stop signs, and make traffic flow more orderly, in this case allow up to 16 pixels (or cars) of data to pass thru before a red light. the during red light time we do math, and let other data thru. this dramatically speeds up lcd writes on spi display. as things don't need to slow down and get back up to speed (the initialization steps to write pixel data). this project compiles using Arduino ide 1.6.5, multi writes work in 1.8.5 also

this project uses code that allows between 4 to 256 rectangles to be written in a single instruction or single set of instructions without need for command mode, windowset, pixel address commands for each pixel. this allows effective and efficient spi data transfer of data with minimal command overhead. for 64 rectangle at a time look at program links.

if using the spi optimized driver files and a st77xx display (i'm optimizing for ILI9341 by end of october 2018) you will get incredible update speeds to display. if using a display other than the ones i have mentioned, i have created a wrapper that takes my display commands and translates them into universal drivers for adafruit. you'll get the performance from the reduced command overhead (4-10x) but wont see the 2x-3x boost that comes with spi bursting, memory optimizations as these need to be built into the display drivers)

video below shows project details and is only 3-4min. This is just for an article format to discuss methods and ways of speeding up lcd displays that use SPI. It explains why SPI is slow, and methods to increase performance by over 30 times! example of use can be found here it is a complete thermal pointer, that works at 64x64 resolution on Arduino mega, 64x64 on uno! There is so much overhead to writing pixel information, writing rectangles reduces this overhead, but now a newer method, writing several rectangles at once with minimal overhead. this alone leads to 16x performance for Arduino, where buffering the entire screen is not an option due to limited memory.  there is also double speed to SPI on Arduino. SPI bursts are now twice as fast! (with everything it is able to do 64x64 at ~10fps!) newer code is in beta section of my downloads for speed with 64x64. 

weird tech jargon below!

Yes some people use buffers but the spi transfer itself is also not optimized. There is often a lot of overhead other than pixels for display of data.

originally code was written per pixel, and being that Arduino code didn’t know where pixel was, each pixel needed an address, so 2 bytes for x, 2 bytes for y address, and for color data, in 16 bits it required 2 bytes of data. Only one time was window size defined, at initialization, or at start of lcd use, the window size was set to full display size.

On Arduino buffering of the entire display is often not possible as the memory requirements are a lot.

There has been a really clever method that came out and it was to write rectangles, as long as data had same color it is faster to write areas of the display. Set windows size commands that takes 6 or more bytes, set start address, and then write same color 2 byte data, again and again. 

The display is usually set to a mode that automatically advances pixel location after color data is sent. This seems faster, but the closer the information gets to the display resolution the slower this method becomes because of the overhead to set the starting positions of the window size for the rectangles. At reasonable resolutions the display update rate gets down to a few frames a second.

What I have is a possible solution to the speed of spi, using the rectangle command to draw several rectangles at once, utilizing the set window command, but allowing rectangle command to write several rectangles within the larger rectangles with individual colors, and only uses up to an additional 32 bytes of ram, but speeds up display writes by up to 16 times because overhead is only used every 4 or 16 pixels, instead of each time!

Also removing the wait of spi. Spi data is usually checked to ensure that the write of serial 8 bit data is complete. Why do we have processor stall and wait for this to complete....

Read more »


this is amg8833 sensor running on an Arduino uno equivalent with software. it uses 23878 bytes flash and 1068 bytes ram. i'm working on software that enhances detail and outputs resolution so far at 64x64 resolution with some built in filtering to reduce noise, and increase detail and auto temp. shown in image is my leg, the temp and surrounding temp with a built in color bar to show range. it is auto ranging temp. it is doing about 10 frames a second at 64x64 detail with subsampling, and filtering features enabled.

Portable Network Graphics (PNG) - 360.78 kB - 09/23/2018 at 10:49



new housing for device. updates on thingiverse this has a base that has better balance and is resistant to tipping.

Portable Network Graphics (PNG) - 49.65 kB - 09/16/2018 at 07:06



this is image of my hand from 64x64 and enhanced mode of thermal cam sensor amg8833 there are still artifacts that i need to remove. files are now removed and linked to locations in github, it allows for proper tracking of updates.

Portable Network Graphics (PNG) - 260.07 kB - 09/09/2018 at 06:56


Newer versions with bugs fixes are on github sketch uses 952 bytes ram 26950 bytes flash, 64x64 sub sample. still fixing bugs, have some block effects, so i think there are errors in how the color information is handled. still shrinking code, and reducing ram. uno has about 5-6k to program other functions.

Zip Archive - 370.78 kB - 09/09/2018 at 06:47


  • whats next.. increase display speed 3X more, get 128x128 subsample on ili9341

    jamesdanielv12/16/2018 at 09:09 0 comments

    i've been spending time getting the mlx90640 ready to work on the thermal cam code, however my main priorities are to change the sub sample code so it runs in any project. my goal for this is to have an input resolution buffer, and all you do is fill the input buffer, and then request what data would be at output resolution line. with caching of math performed, most calls to reference addresses will return data instantly.

    since the sub sample math is non float, it solves rather quickly compared to the display writes.

    once i separate out the drivers and have references to display location and buffering be able to write any pixel at a time (right now it needs to be an entire square block), i can reduce the commands settings for display to 1 time per entire line. this will speed up writes considerably, because most of the write time is still the command overhead, and after this change, i will set a window to 1x240, or 1x320, and write all the pixels in the line, this should allow update refresh rates of about 30-50 FPS, or 60-100 FPS interlaced mode.

    also i need to get ili9341 display upsampling to 128x128 for amg8833.

    i'm not sure what order this will happen but the 128x128 is easier as i already have the st77xx able to do this.

  • added ability to show where high and low temp is on display.

    jamesdanielv12/16/2018 at 08:52 0 comments

    should be simple, low temp square is white,  high temp square is a yellowish orange. keep in mind that this is working using an arduino uno, it is upsampling a amg8833 to 64x64 square blocks in a 320x240 display real time!

  • i have the mlx90640 sensor reading full array of calibrated values in deg c

    jamesdanielv12/09/2018 at 19:35 0 comments

    i think i've got the mlx90640 sensor working to read deg c outputs after running a buttload of calculations. i will know for sure when i convert the code to output colors on lcd display.

    code runs a lot of calculations and outputs degrees in C. sensor is capable of resolution to 0.00065 deg c! however noise is far higher than that and some work on speed of calculations and noise reduction is in the works.

    I will begin to convert the code into a drop in replacement coding for the amg8833 sensor code and make it work with the thermal cam.

  • added ability to calibrate temp easy

    jamesdanielv11/30/2018 at 22:03 0 comments

    more of a fine tune offset

    just change the setting tempOffsetFloat 2.0 to a lower or higher number then upload sketch. 

    tempOffsetFloat is in deg C.

  • added a details sheet to code that shows what settings do

    jamesdanielv11/28/2018 at 17:03 0 comments

    added a details sheet to code that shows what settings do, and some settings that will be removed soon.

    //thermal cam setting choices and settings

    lcd pins 

    TFT_CS. //fewer issues if leave pins the same. might need to change some pin settings in 

    TFT_RST//st77xx.cpp or ADAFRUIT_SPITFT_ili9341.cpp


    interlaced - true or false. this changes updates to cells either left to right or every other cell block. it prevents the rolling effect if display update rate drops.

    blocKinessRemovalFilter 0-10. zero is disabled. this blends the colors of pixels a little. it helps with blockiness effect. works better at higher resolutions and is only enabled if above 16x16

    autorange- true or false. if set true this changes. min and max temp to ram values and it moves entire color range within the values in the sensors. autorange adjusts depending on what looked at and allows higher sensativity generally.

    optics_correction-true or false. this is a trick to compensate on the different angles or slight different positions of the sensors in array. if this is turned off especially at lower resolutions you will notice artifacting. sensors are not perfectly placed.

    threshold- 0 -100. this ignores values below it and treats them as zero. this is to reduce noise. this is also before the autorange which changed values to a temp range.

    enhancedDetail- true or false. this looks for changes within a sensor that would normally just average out. it increase detail resolution to about 16x16 in some situations. it takes advantage that light  intensity is predictable, and if it does not follow predictable pattern a shadow exists in between.

    SubtractColor- 0-500. if zero it is disabled. this is a post processing step that says after temp range is calculated and spread across 1024 intensity levels, we want to reject the amount. this allows low changing areas to be filtered out for a cleaner image.

    optimize- 0-3. this changes the methods of optimizations to reduce screen writes, such as buffering, bandwith control to only update most changed as small change is usullly noise, and special write patterns that optimize pixel writes to display. higher is better

    interpolatemode 0-3, maybe 4 or more. this is the level of detail of sub sample. 0=8x8 pixel blocks, 1=16x16 pixel blocks, 2=32x32 pixel blocks, 3=64x64 blocks……

    noisefilter- 0-200. this is only enabled if optimize is 2 or higher. this looks at temp data of a single sensor and only updates it if greater than this value. it reduces noise to display, but too high and detail will be lost. if set too low there will be a lot of snow on screen, 

    show_temp_readout- true or false. if true this allows temp infomation to show in center of display and it averages center 4 temp sensors together to get value. depending on sensor used this can be +/- 4 degrees. 

    temp_Fahrenheit- true or false. if true temp is in F, if false it is in C

    showcolorbar-true or false. if true this shows a color bar map and lowest temp and approx highest temp on screen

    colorMode 0 and up. these are color map profiles. you can make your own using colorgenerator.html file 

    spi_optimized_st77xx- true or false. at lower resolutions this takes advantage of spi bursting doubling speed

    spi_optimized_st77xx-true or false. this uses slower pushcolor and setaddresswindow commands that should be universal but some drivers do not support it. it allows other displays to work for testing purposes, but does not include the spi optimizations

    subpixelcoloroptimized- -1,0,1 this command is going away soon. -1 allows commands without lcd overhead for timing and testing, 0 writes 1 pixel at a time, and 1, writes 4 pixels at a time. this currently only works to 16x16 resolution. 


    Read more »

  • doubled ili9341 performance in 64x64 subsample mode

    jamesdanielv11/28/2018 at 08:58 0 comments

    this version has several enhancements, such as doubling text writes to display which were slow and improving the draw performance of the color bar, and significant performance boost of the graphics showing of the upsample amg8833 thermal sensor output.

    i also now turned off debug serial for display block time updates. this should speed things up more. sending a capitol 'C' to Arduino serial will send a copy of 8x8 sensor output. 

    here is it in action.

  • fixed display bar for different display resolutions

    jamesdanielv11/25/2018 at 09:00 0 comments

    this image is of my hand, on a ili9341 display that upsamples a amg8833 sensor

    64x64 with display temps showing centered and color bar, now it calcs positions based on display size rather than fixed positions. also tuning the color visual separation that makes things such as fingers easier to see. keep in mind the amg8833 is only a 8x8 sensor. getting this much detail out of it. it works on Arduino uno or equivalent. still needs 64x64 optimized.

  • ili9341 has amg8833 to 64x64 resolution. will optimize soon

    jamesdanielv11/25/2018 at 05:27 0 comments

    ili9341 display now has 64x64 upsample. the drivers are not optimized yet, and soon will be.

    one of the most basic changes was the first color in the table 21 is black. i will update web color generator with the option to put 1st color in table to black. this allows low noise and rejected color to go to black. 

    I also have included changes to AdvancedColorVisualSeperation, that allow detection of slight temperature difference, and enhances changes. this is good for smaller details such as fingers together now can be seen as separate items.

    by default if   AdvancedColorVisualSeperation is on,  lowerColorvalues ofsets the color intensity to be in a more centered range. i could make this all automatic, but i set it so it could be experimented with.

    also i turned up SubtractColor to 280, it prevents the lower intensitys from being calculated, more so because of noise.

    anyway the ili9341 display updates about 6fps currently (12fps interlaced)  i will update drivers soon that will double performance. currently setting optimize 2 or optimize 3 does nothing because the code is not there to distinguish the difference for 64x64 mode. will work on it soon.

    I'm also in the middle of centering the display information and temp map color bar. if you want it disabled currently set this:

    show_temp_readout false

    Also you may start seeing more code relating to the mlx90460. it will be working soon, and ill have a guide to the changes and meanings for the settings.

  • improving filtering. will include optical offset. still trying for better filtering

    jamesdanielv11/11/2018 at 00:29 0 comments

    the 128x128 subsample method is noisy. i'm trying to resolve this by fixing possible issues using simulation.

    i'm currently simulating upsample in javascript code.  ill post a link when web version is uploaded and article is up for the non float upsample method used. i thought it would have been uploaded a week or so ago but  it will be out this next week sometime. i hope to have it capable of 32x32 (the simulation) and eventually 128x128. this simulation code will help me fix noise issues at 128x128 and possibly greater upsample resolutions.

    i have already included optical correction which compensates for individual sensor variation of angle. this effects and improves the pixel color accuracy. i included compensation on a hunch, and with trial and error it proved to be the correct thing to do. it slightly averages pixels together about  1/16 depending on specific settings in code. this is before subsampling is used. 

    there is an other effect that i am going to implement that will make image upsampling have less shadowing and it is pixel offset calculation. depending on position of sub sampled pixel there should be a set amount of offset to its brightness value compared to its neighbors. 

    i would have posted more results sooner, but i ran into this optical error, it can be viewed in both upsampled views and shows with a darker and lighter shadow in both 16x16 and 16x16 corrected. 

    i have spend a little bit of time trying to see if i could make this better. granted in real life such a sharp contrast in backgrounds would most likely not be possible, but i spent time to see if there was something that could be easily done to make it look better. 

    after bit of trial and error i found out that even with other upsample methods this specific sharp contrast and small size of image had issues with other upscale methods. so it is not just my method that has this issue, it still bugs me, but not as much as it did. part of the issue is no shadow information in image. this would not be an image in real life. 

    here below is how it is output thru gimps upsampling algorithms from 8x8 grid to 16x16 grid, image was cut and pasted from screen snap shot of image scaled to screen 1600%

    I will see how this algorithm works to see if improvements can be made in my subsample method.

  • ili9341 320x240 at >10fps(20fps interlaced) 32x32 interpolated blocks) using amg8833 sensor

    jamesdanielv11/07/2018 at 02:22 0 comments

    beta release of ili9341 display working at an average of 10fps (20fps but interlaced). need to finish up feature changes and buffer size adjustments for mlx90460 sensor, then get 64x64 mode working for ili9341 display.

    including new features on 32x32 mode such as AdvancedColorVisualSeperation. setting this with a number greater that 0 enables it. whatever number above what you set to is the color range intensity that is amplified, so unlike narrowing temp range which also brings more noise, this just shows more details around objects. im still working on methods that handle saturation, currently i'm just clipping it at max intensity false color value

    and optimize 3 setting that reduces display writes by half, and some other minor tweaks.

    also starting to put in code that allows sensor buffer size to change. this is important for adding mlx90460 sensor to be used in code in future.

    to turn on and off AdvancedColorVisualSeperation set it to true or false. it can saturate images.

View all 38 project logs

Enjoy this project?



jamesdanielv wrote 12/10/2018 at 01:28 point

mlx90640 sensor now reading degrees c in array mode thru terminal. working on optimizing and simplifying math and cleaning up routines. then making it work with thermal cam code

  Are you sure? yes | no

jamesdanielv wrote 09/30/2018 at 06:41 point

adding the mlx90640 sensor to be able to work with this project has been complex. so complex it has its own article blog here 

currently sensor outputs to terminal and shows a visualization of all sensors in a grid pattern similar to a picture. i have added ability to change hz, ad resolution and switch between continuous and step mode (single shot mode) as well as read sensor values and compare them to values stored in progmem (created another program that extracts them for cut and paste)

  Are you sure? yes | no

jamesdanielv wrote 09/26/2018 at 06:30 point file , that is linked here

shows how to access registers for modes and status of sensor and read what page it is on. it reads a full 32 pixels at a time. as long as reading of memory is after both passes has completed then all data will be there, even reading one byte at a time. 

this sketch currently uses 706 bytes and 5720bytes flash

  Are you sure? yes | no

jamesdanielv wrote 09/26/2018 at 03:47 point

added link to test file for Arduino mlx90640 sensor. video showing sensor working on Arduino uno equivalent

demo uses 522 bytes ram, and 4260 bytes flash. 

  Are you sure? yes | no

jamesdanielv wrote 09/23/2018 at 10:48 point

fixed further issues with 64x64 (blockiness), and filters to reduce noise when enhancing sensor colors. code still needs clean up, but again ill wait till 128x128 resolution.

going to test more and then put it in the regular software branch.

some new working settings

threshold// settings to have level below a certain amount show as lowest color to reduce noise 

SubtractColor//similar to threshold but post processing. removes darker colors

autorange true//this allows sensor temps to be auto calibrated to what is on screen

showcolorbar //this allows lcd to show colors used on screen and in a color bar map

showtemp // shows temp and also at left and right side of color map bar

adding a new photo showing device working in 64x64 mode with color bar. look at files section for photo

  Are you sure? yes | no

jamesdanielv wrote 09/22/2018 at 04:16 point

i'm uploading an example file for Arduino that takes a row of sensor data from the MLX90640 sensor and outputs different dot sizes to terminal, so you can see your hand cross sensor. what i like about this sensor is you can instruct it to send only the data you want to use at a time. i have not incorporated calibration into sensor. any ways this is the first example out there for Arduino uno (in my case duemilanove) . it uses about 4k flash and 480 bytes. it needs clean up that will happen when i post to its own github. at this point it is functional. quite ugly but it works. ill continue playing around with it, but it is a side show until i get amg8833 at 128x128

  Are you sure? yes | no

jamesdanielv wrote 09/17/2018 at 23:14 point

forgot to mention, i added ability to capture screen data by just sending 'c' ascii 67 in serial  monitor, this will return values of all sensors in a single shot. it helped me resolve some of the issues with the color banding that was causing blockiness issues at 32x32. with auto temp feature enabled this output is usually within 0.5 degrees of min and max temp on screen and is sent as floats.

  Are you sure? yes | no

jamesdanielv wrote 09/17/2018 at 08:53 point

still able to keep ram around 1k for sub sampling up to 64x64. 128x128 is a work in progress

resolved blockiness issues up to 32x32 and offset errors. still working on resolving issues with 64x64 blockiness. i think what i need to do is reduce error from calculations for pixtel color and position space. with multiple divides, there is greater error in pixel color. the div used is basically a divide by 2 or a >>1  (shift right). so i think resolving the rounding errors will resolve this for higher resolutions. either by starting off with a value *8 or 16 (8 or 16 times higher to allow more resolution detail), or just noting the error and adding it in later. the rounding error is from the 1 bit resolution detail of the calculations, no float. again issues are resolved for up to 4*4 sub sampling of pixels, but to do 8*8 sub sampling or 16*16 requires rounding considerations, or so that is my current thought... will post when 64x64 sample from 8x8 matrix blockiness is removed.

i'm still using 3 pixels for calc of sub sampling because it is superior to bi-polar sample with sharper detail.

i have resolved issues in pixel position calculations and removed the cross effect for up  to 64x64 resolution. pixels on screen are averaged differently that side pixels. still this is fastest method avail on Arduino and currently for up to 4x4 sub sample calculations have no visible difference, and more clarity than other methods of pixel sub sampling. after 64x64 sub sample errors completely fixed will post a video on how other people can do high speed non float sub sampling.

  Are you sure? yes | no

jamesdanielv wrote 09/07/2018 at 09:47 point this build has 64x64 sub sampling using a total of 920 bytes of ram for entire sketch. 64x64 res of screen is 8x8 samples, sub sampled with each pixel measurement as a 8x8 sub sample. this runs as fast at 64x64 as previous version did at 32x32. however.. i need to troubleshoot color values on edges. i'll have it fixed soon. 32x32 works great. i have added in enhancement features that enhances color changes, and shadows. so smaller objects can be seen. i know now it is possible to run 128 x128 resolution detail from arduino, as memory overhead is no longer a limit. buffer size of 32x32 is done with 40 bytes, 64x64 ram buffer is is about 132 bytes, but sketch >34k is too large for 64x64 for uno. will fix that. working on shrinking code down with minimal penalty. 

  Are you sure? yes | no

jamesdanielv wrote 09/04/2018 at 21:55 point

I'm currently using the amg8833 chip, I'm also now looking into compatible with the MLX90640. I have some more info on the voltage compatibility issues with using the MLX90640 directly with Arduino and use of sparkfun board with MLX90640 on it: the resistor values on the i2c lines (sda, scl) are just 2.2k pull up resistors. however looking thru documentation of data sheet MLX90640 section 13.1 confirms that the i2c is 5v tolerant  and in spec (2.6v-5v). it may even work without pull up resistors if the Arduino internal pullups are used (20k). if using part without sparkfun board, i can only recommend use of 2.2k resistors because this will be my setup.

  Are you sure? yes | no

jamesdanielv wrote 08/20/2018 at 21:09 point

yes, i saw a similar sensor on sparkfun in theory the code should work by just changing array size, but it will likely run into memory issues as of 8_21_18. I am working on reducing memory requirements of sub sample.  as for arrays size change feature You can thank for that as is referenced to adafruit's. might run into issues with memory avail, but i'm working to remove need for most of buffers for sub sampling. that in itself is worthy of an other article. non float sub sampling. just to be clear i am not sponsored by adafruit or sparkfun.  as always thanks for the information about other sensors.

  Are you sure? yes | no

Chase Rayfield wrote 08/20/2018 at 20:27 point is an interesting part.... 24x16 for not much more than this 8x8 sensor.

The 55 degree FOV model is in stock the 110 degree one is not.

  Are you sure? yes | no

jamesdanielv wrote 09/02/2018 at 02:55 point

hey i checked on those sensor prices from future electronics and they are only if you buy 20 at a time. the 8x8 sensor is reasonably priced for just one at a time purchase.

  Are you sure? yes | no

jamesdanielv wrote 09/02/2018 at 23:19 point

sold out? i don't know. it is easy to overlook qty requirements as i did several times. but that is useful info on digikey so thanks. i've got one of these sensors on the way (MLX90640) the good thing is many of these sensors keep there resale value. it is worthy to know that the voltage input needs to be 3.3v. (not a problem if hooked up to Arduino 3.3v, or using a teensy, or equivalent)  I'm a little concerned about the compatible input and output voltages grey area of the sensor and the Arduino, but will base my code around Arduino still as i think playing favorites with mcu's doesn't win for anyone.  Making something work on Arduino usually guarantees compatibility even if it may be a little work for the end user of different product. if voltage logic  is the harry edge with voltages it has some unique issues especially with latency and propagation delays, especially if the slew rate is slow (not strait up and down logic signal). this means it will either work, work only at a slower speed, or just add in a $0.50 buffer to scale voltage, or use a low voltage mcu chip.  as always from you or anyone feel free to correct. i would love to be wrong about this! just to be clear i am not recommending a specific platform, just testing and making cool stuff work on Arduino...  i should add that i ended up purchasing a because it came on a board, with what i believe is logic to allow for voltage difference. in this case i think just a voltage divider over the input pin of the sensor ic2. so for example 2 1k resistors may work in place of buffer. the board is not compatible with uno but it is because of math and memory requirements. hopefully i'll change that.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates