<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>blinkenlights</title>
	<atom:link href="https://rodyne.com/?feed=rss2&#038;cat=152" rel="self" type="application/rss+xml" />
	<link>https://rodyne.com</link>
	<description></description>
	<lastBuildDate>Fri, 06 Feb 2026 07:12:22 +0000</lastBuildDate>
	<language>en-NZ</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.7</generator>

<image>
	<url>https://rodyne.com/wp-content/uploads/2015/11/web-header2.png</url>
	<title>blinkenlights</title>
	<link>https://rodyne.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Small Blinkenlight Update</title>
		<link>https://rodyne.com/?p=3533</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 19 Jan 2026 22:05:55 +0000</pubDate>
				<category><![CDATA[blinkenlights]]></category>
		<category><![CDATA[Other Programming]]></category>
		<category><![CDATA[PicoMite]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Retro]]></category>
		<category><![CDATA[RP2040]]></category>
		<category><![CDATA[MMBasic]]></category>
		<guid isPermaLink="false">https://rodyne.com/?p=3533</guid>

					<description><![CDATA[I loaded the MM Edit program onto my new Linux/Kubuntu desktop and wanted to test it out with a Raspberry Pi Pico development board to ensure everything was working. As my Pico board was all the way over in my &#8230; <a href="https://rodyne.com/?p=3533">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I loaded the MM Edit program onto my new Linux/Kubuntu desktop and wanted to test it out with a Raspberry Pi Pico development board to ensure everything was working. As my Pico board was all the way over in my workshop, and it was raining and cold outside (yeah, middle of summer here &#8211; go figure!), I pulled the spare <a href="https://rodyne.com/?p=1751">RP2040 blinkenlight board</a> off my spares shelf and decided to have another go at the temporary C firmware on it, maybe fix it once and for all.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="784" height="168" src="https://rodyne.com/wp-content/uploads/IMG_0508-ezgif.com-optimize.gif" alt="" class="wp-image-3540"/></figure>



<p class="wp-block-paragraph">The original blinkenlight board was a surprise favourite with the Hacker News and HackaDay community last year, and judging from the correspondence I had, a lot of people appreciated the RP2040 version over the original 8-Bit PIC one. Obviously, as I was testing MM Edit, not VS Code, I replaced the C program with a small MMBASIC program, just to see how easy it was to to do, if it all worked, and, if BASIC was fast enough to drive a bunch of multiplexed LED&#8217;s with row and column drivers. I am pleased to say it did, and the results surprised me, this must have also been the fastest development I have ever done, maybe 3 hours start to finish! It was as simple as clicking the boot-select switch on my board, plugging it in to a free USB port, and dropping the PicoMite RP2040 6.01 firmware on the new USB drive. Then writing the super-simple program below in MM Edit, pressing reset on the blinkenlight board clicking the MM Edit upload button and presto!</p>



<p class="wp-block-paragraph">I wont even bother to explain the code you don&#8217;t get much easier than this. I guess I should start testing my C compilers now, wont be as much fun <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f641.png" alt="🙁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<pre class="wp-block-code"><code>' Firmware for the Blinkenlights board (https://rodyne.com/?p=1751)
' Note we are ignoring the 4-switch inputs as not required in USB version
' Use OPTION HEARTBEAT OFF to free up the heartbeat LED which I have used

OPTION EXPLICIT     ' must declare variables and defaults
OPTION DEFAULT NONE '
ON ERROR IGNORE     ' lazy error handling for non-critical hardware

' set RP2040 pin assignments
SETPIN gp3, dout
SETpin GP4, dout
SETpin GP5, dout
SETpin GP9, dout
SETpin GP10, dout
SETpin GP11, dout
SETpin GP12, dout
SETpin GP13, dout
SETpin GP14, dout
SETpin GP15, dout
SETpin GP16, dout
SETpin GP17, dout
SETpin GP18, dout
SETpin GP19, dout
SETpin GP20, dout
SETpin GP21, dout
SETpin GP22, dout
SETpin GP23, dout
SETpin GP24, dout
SETpin GP25, dout
SETpin GP26, dout
SETpin GP27, dout
SETpin GP28, dout
SETpin GP29, dout

' global variables
dim integer regs(8) ' 8 banks (rows) of 16 leds
DIM integer row = 0 ' which row is selected for output
dim integer OpModeTimeout = 0 ' it needs to accept input from my retro comp
dim string  CBuff$="" 'input temporary sorage
dim string  Char$
DIM integer trnd, nVal, hVal

' program main loop
DO
  if OpModeTimeout=0 then ' no input so do some random light show
    trnd = rnd*400 ' adjust this to speed up or slow down light show
    if trnd&lt;10 then regs(row)=regs(row)&lt;&lt;1  ' shift bank LEDs left
    if trnd=13 then regs(row)=regs(row)>>1  ' and right
    if trnd=14 then regs(row)=not regs(row) ' invert them
    if trnd=15 then regs(row)=rnd*65000     ' pop a new random pattern in
    if trnd>380 then regs(row)=regs(row)+1  ' increment them
  else
    OpModeTimeout=OpModeTimeout-1 ' if no input for 12s then timeout to rnd light show 
  endif  

  ' asset the scan row driver (1=ROW ON)
  pin(gp22)=(row=7)
  pin(gp23)=(row=6)
  pin(gp24)=(row=5)
  pin(gp25)=(row=4)
  pin(gp26)=(row=3)
  pin(gp27)=(row=2)
  pin(gp28)=(row=1)
  pin(gp29)=(row=0)
  
  ' decode and asset the correct column driver for the 16 LED's (0=COL ON)
  if regs(row) and &amp;h0001 then PIN(GP3)=0 else pin(gp3)=1
  if regs(row) and &amp;h0002 then PIN(GP4)=0 else pin(gp4)=1
  if regs(row) and &amp;h0004 then PIN(GP5)=0 else pin(gp5)=1
  if regs(row) and &amp;h0008 then PIN(GP9)=0 else pin(gp9)=1
  if regs(row) and &amp;h0010 then PIN(GP10)=0 else pin(gp10)=1
  if regs(row) and &amp;h0020 then PIN(GP11)=0 else pin(gp11)=1
  if regs(row) and &amp;h0040 then PIN(GP12)=0 else pin(gp12)=1
  if regs(row) and &amp;h0080 then PIN(GP13)=0 else pin(gp13)=1
  if regs(row) and &amp;h0100 then PIN(GP14)=0 else pin(gp14)=1
  if regs(row) and &amp;h0200 then PIN(GP15)=0 else pin(gp15)=1
  if regs(row) and &amp;h0400 then PIN(GP16)=0 else pin(gp16)=1
  if regs(row) and &amp;h0800 then PIN(GP17)=0 else pin(gp17)=1
  if regs(row) and &amp;h1000 then PIN(GP18)=0 else pin(gp18)=1
  if regs(row) and &amp;h2000 then PIN(GP19)=0 else pin(gp19)=1
  if regs(row) and &amp;h4000 then PIN(GP20)=0 else pin(gp20)=1
  if regs(row) and &amp;h8000 then PIN(GP21)=0 else pin(gp21)=1

  pause 1 ' 1mS
  
  ' after pause turn all scan lines off so we dont get ghosting when we change states
  pin(gp3)=1
  pin(gp4)=1
  pin(gp5)=1
  pin(gp9)=1
  pin(gp10)=1
  pin(gp11)=1
  pin(gp12)=1
  pin(gp13)=1
  pin(gp14)=1
  pin(gp15)=1
  pin(gp16)=1
  pin(gp17)=1
  pin(gp18)=1
  pin(gp19)=1
  pin(gp20)=1
  pin(gp21)=1
  
  ' process banks/rows 0-7 in round-robin
  row=row+1
  if row>7 then row=0
  
  ' The USB port in this firmware acts like a virtual terminal so you can send
  ' text strings into it as if you are coming from a serial port, just need to 
  ' pretend a serial char is a keypress :-)
  ' format if r=nnnn where r is the row (0-7), and nnnn is a 16-bit hex string
  ' eg "4=2FE7&lt;CR>" will put the value 2FE7 into bank 4 of the leds
  ' if no serial received for more than 12 seconds then revert back to rnd light show
  Char$=inkey$
  if Char$&lt;>"" then ' check if character received
    if Char$=CHR$(10) OR Char$=CHR$(13) then ' if its CR then process buffer
      ' Note I'm not doing any checks on input using lazy error handling
      print "'"+CBuff$+"'" 'for debugging
      nVal = Val(LEFT$(CBuff$,1)) ' get the register value 0-7
      hVal = val("&amp;H" + mid$(CBuff$,3,4))' convert 4 char hex str to value
      regs(nVal)=hVal ' set the register
      OpModeTimeout=10000 ' reset the timeout to display it
      CBuff$="" ' clear buffer string when processed 
    else  
      CBuff$=CBuff$+Char$ ' if not a CR then add char to buffer string
    endif  
  endif  

loop
</code></pre>



<p class="wp-block-paragraph">Schematic and Gerbers for the Blinkenlights RP2040 = &gt; <a href="https://rodyne.com/wp-content/uploads/Schematic_Gerbers_SourceCode_and_UF2_Binary.zip">here</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Das Blinkenlights (mit RP2040!)</title>
		<link>https://rodyne.com/?p=1751</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sun, 26 Jan 2025 09:17:55 +0000</pubDate>
				<category><![CDATA[blinkenlights]]></category>
		<category><![CDATA[RP2040]]></category>
		<category><![CDATA[ChatGPT]]></category>
		<guid isPermaLink="false">https://rodyne.com/?p=1751</guid>

					<description><![CDATA[Got my updated design back from JLPCB on Friday and this morning decided to test them and have a go at programming them. While I have done a hardware RP2040 design for a customer before I have never actually gotten &#8230; <a href="https://rodyne.com/?p=1751">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Got my updated design back from JLPCB on Friday and this morning decided to test them and have a go at programming them. While I have done a hardware RP2040 design for a customer before I have never actually gotten around to programming one.</p>



<figure class="wp-block-image size-full"><a href="https://www.youtube.com/watch?v=vKjqw5iGqnQ&amp;t=2s"><img decoding="async" width="919" height="944" src="https://rodyne.com/wp-content/uploads/Untitled.jpg" alt="" class="wp-image-2579" srcset="https://rodyne.com/wp-content/uploads/Untitled.jpg 919w, https://rodyne.com/wp-content/uploads/Untitled-292x300.jpg 292w, https://rodyne.com/wp-content/uploads/Untitled-768x789.jpg 768w" sizes="(max-width: 919px) 100vw, 919px" /></a></figure>



<p class="wp-block-paragraph">The changes to the <a href="https://rodyne.com/?p=1674">original blinkenlights</a> are:</p>



<ol class="wp-block-list">
<li>RP2040 Microcontroller </li>



<li>Reversed the numbering on the silk screen (Once I saw this mistake I could not unsee it!)</li>



<li>Connects via USB cable (No host PC required, can just be used for power and it will blink)</li>
</ol>



<p class="wp-block-paragraph">The first thing of note is that the guy I made the boards for wanted the RP2350 (the worthy successor to the RP2040), but that chip is unfortunately not available anywhere, though rumours are they are on their way and may be in stock by the time you read this! Why the guy wanted a chip so capable for basically blinking some LED&#8217;s is beyond me, but he was so keen to get the boards he went for the older RP2040, which is still far too powerful for the task, however with both chips being under US$1.00 I guess I should just stop asking stupid questions!</p>



<p class="wp-block-paragraph">The design is a bit easier as the RP2040 has 30 GPIO pins, this means we don&#8217;t have to resort to any weird external chips and can just drive the 8 rows of 16 LEDs directly through MOSFETS, though I need to move everything from 5V to 3.3V. The spare pins connected to a 4-pin dip-switch the only use at the moment for this is to ensure differing panels have different random light sequences but could also be read by the host to send different info to different panels if this functionality is programmed.</p>



<p class="wp-block-paragraph">As the guy is away for a few weeks I decided I would check them out myself. Previously I have checked the RP2040 is working by simply setting the BOOT SEL switch, to disable the flash chip, and plugging the USB into my PC. If all was well, a new drive would appear in the file manager as a mounted USB Drive with two readable files. As this part worked I thought I would try and be smart and take a ride with ChatGPT to see if I could convert my original PIC C code to the RP2040.</p>



<p class="wp-block-paragraph">TLDR; The hardware is fine, the LEDs blink, but buggered if I could get the USB/CDC serial interface working in the few hours allocated.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="529" src="https://rodyne.com/wp-content/uploads/blinkenlights-rp2040-pcb-1024x529.jpg" alt="" class="wp-image-1752" srcset="https://rodyne.com/wp-content/uploads/blinkenlights-rp2040-pcb-1024x529.jpg 1024w, https://rodyne.com/wp-content/uploads/blinkenlights-rp2040-pcb-300x155.jpg 300w, https://rodyne.com/wp-content/uploads/blinkenlights-rp2040-pcb-768x397.jpg 768w, https://rodyne.com/wp-content/uploads/blinkenlights-rp2040-pcb-1536x793.jpg 1536w, https://rodyne.com/wp-content/uploads/blinkenlights-rp2040-pcb.jpg 1915w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Note I decided to move the BOOT SEL switch back to a push-button in the final design, even though the DIP switch did make it easier to use without having three hands.</p>



<p class="wp-block-paragraph">If you want to make this project, again all the files are here =&gt; <a href="https://oshwlab.com/rodyne/new-project_copy_copy_copy_copy">OSHWLab</a></p>



<p class="wp-block-paragraph">If you don&#8217;t like that, then all the files are also zipped up here =&gt; <a href="https://rodyne.com/wp-content/uploads/Schematic_Gerbers_SourceCode_and_UF2_Binary.zip">zip-files</a></p>



<p class="wp-block-paragraph">No licence, everything public domain.</p>



<p class="wp-block-paragraph"><span style="text-decoration: underline;">My journey with a new Microcontroller and ChatGPT. (from zero to hero!</span>)</p>



<p class="wp-block-paragraph">OK so this morning I knew as much as your average PIC programmer about VS Code, Pico SDK&#8217;s or any of the workings of the RP2040 outside of the (meager) hardware design reference which I used 18 months previous for another board design.</p>



<p class="wp-block-paragraph">After having my usual Sunday stroll up Blenheim&#8217;s own Mount Vernon, a 400M high stroll, with a bunch of other old farts, I sat down at my PC at 1PM and opened the <a href="https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf">getting started with the Pico</a> guide, followed the quite good instructions and compiled my first blink program in C.</p>



<p class="wp-block-paragraph">The only bits that caused an issue were the fact I have never used VS Code before and the first project I created took about 10 minutes to even show up as it silently downloaded the SDK. Of course me being impatient closed VS Code after a minute of nothing seeming to happen, then I noticed the little messages at the bottom right of the IDE and decided to let it do its thing. All good after then, though man, that IDE is really slow!</p>



<p class="wp-block-paragraph">With blinky LED under the belt I decided to ask ChatGPT to create me a bare RP2040 C Program with a 1mS interrupt, which it nicely did, and I proceeded to modify the interrupt to put the LED scan code. Pressing compile totally failed as I forgot about the make file, CMakeList.txt. Now make files are usually handled by whatever IDE I am using, MPLABX or STM32 cube, I never need to touch them and never do anything fancy with them, having it exposed to a newbie like me was a bit of a shock, (Sigh, why did Pascal never catch on!) luckily ChatGPT to the rescue.</p>



<p class="wp-block-paragraph">Now ChatGPT has been pretty good and getting better, but I know it&#8217;s limitations. The perfect example of this was a few weeks ago after it gave me the perfect description how to connect some 74LS193 counters on my retro-computer. A very upbeat naive version of me then asked it for a schematic, and what I got was below!</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1024" height="1024" src="https://rodyne.com/wp-content/uploads/ChatGPT-I-want-to-connect-2-74ls193-counters-in-series-to-get-an-8-bit-counter-can-you-show-me-the-schematic-to-do-this.jpg" alt="" class="wp-image-1753" srcset="https://rodyne.com/wp-content/uploads/ChatGPT-I-want-to-connect-2-74ls193-counters-in-series-to-get-an-8-bit-counter-can-you-show-me-the-schematic-to-do-this.jpg 1024w, https://rodyne.com/wp-content/uploads/ChatGPT-I-want-to-connect-2-74ls193-counters-in-series-to-get-an-8-bit-counter-can-you-show-me-the-schematic-to-do-this-300x300.jpg 300w, https://rodyne.com/wp-content/uploads/ChatGPT-I-want-to-connect-2-74ls193-counters-in-series-to-get-an-8-bit-counter-can-you-show-me-the-schematic-to-do-this-150x150.jpg 150w, https://rodyne.com/wp-content/uploads/ChatGPT-I-want-to-connect-2-74ls193-counters-in-series-to-get-an-8-bit-counter-can-you-show-me-the-schematic-to-do-this-768x768.jpg 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">So WTF! Yes it will make a good t-shirt to mock our AI overlords in the future, but as it stands, AI is not quite at the level of replacing an EE. (yet!)</p>



<p class="wp-block-paragraph">Anyway, back to the story. I was a bit shocked with the number of configurations you needed in the CMakeList.txt file, e.g. just including a file in the C code before was good enough, but the compiler here will not do anything unless the make file knows about it. I guess there is some logic, since there are about a million C libraries out there.</p>



<p class="wp-block-paragraph">Anyway, to my surprise on first compiling my program and dropping the UF2 file onto my RP2040 USB drive it worked! Hi Five to me and ChatGPT, the winning combo!</p>



<p class="wp-block-paragraph">Except it didn&#8217;t work as none of the USB serial part worked and no matter what prompts I fed to it, I did not get a resolution to my problem. But the lights blink magically!</p>



<p class="wp-block-paragraph">Anyway it does seem a nice chip to work with, and I may look into it (and the RP2350 when available) a bit more for my home and professional projects in the future. As for ChatGPT, it is scarily good, and like a good politician very persuasive even when it is wrong, unlike a politician though it should be able to fix its mistakes. Welcome to the future!</p>



<p class="has-blue-background-color has-background wp-block-paragraph"><strong>Note from future self: I fixed the code in about an hour in MMBasic => <a href="https://rodyne.com/?p=3533">here</a>, it might be a lot easier to understand if C is not your scene and/or you just want it working.</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Das Blinkenlights!</title>
		<link>https://rodyne.com/?p=1674</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sat, 11 Jan 2025 08:50:47 +0000</pubDate>
				<category><![CDATA[blinkenlights]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[bling]]></category>
		<category><![CDATA[Front panel]]></category>
		<category><![CDATA[LEDs]]></category>
		<category><![CDATA[PIC]]></category>
		<guid isPermaLink="false">https://rodyne.com/?p=1674</guid>

					<description><![CDATA[Note there is a RP2040 version of this now available Das Blinkenlights (mit RP2040!) Probably the only German word I know, and, quite by accident, became the title of my last hobby project, a project to give some bling to &#8230; <a href="https://rodyne.com/?p=1674">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><em>Note there is a RP2040 version of this now available <a href="https://rodyne.com/?p=1751">Das Blinkenlights (mit RP2040!)</a></em></p>



<p class="wp-block-paragraph">Probably the only German word I know, and, quite by accident, became the title of my last hobby project, a project to give some bling to my boring local RPI-based rack server without impacting the power budget too much.</p>



<p class="wp-block-paragraph">Like every project I seem to start in my life these days, I did not start out to do this, the fact is I have been beavering away for years on a retro 16-bit microcomputer, never really making much progress as I progressively get bored of doing it, find something a bit more exciting to do or get distracted by work. Then one-day out of the blue I woke from a dream of why I was actually doing this, and in a word it was nostalgia.</p>



<p class="wp-block-paragraph">Yes, nostalgia for the long gone days of my working on old mainframe and mini-computers, where I understood the computer literally from their TTL gates upwards, and especially nostalgia for all those flashing lights that made the young me all wobbly at the knees.</p>



<p class="wp-block-paragraph">So what if I skipped my project forward a few years and made the debug lights first! then I could use them on my NAS server.. Sounds like a good plan Boz, and its summer in NZ so the man-cave is nice and warm!</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Raspberry Pi Retro-mainframe server with blinkenlights!" width="640" height="480" src="https://www.youtube.com/embed/vKjqw5iGqnQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p class="wp-block-paragraph">Looks cool, yet it is just my RPI NAS inside the cabinet, a lot of space and the total power budget is 6.5 watts. (including the RPi4 !)</p>



<p class="wp-block-paragraph">The thing that has made this possible is that the price of making and assembling a printed circuit over the past few years has plummeted. The 2U panels I made are really just 1.6mm thick black PCB&#8217;s with all the components mounted on the back and 128 reverse LEDs shining through the front as per the picture below.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="674" src="https://rodyne.com/wp-content/uploads/front-panel.jpg" alt="" class="wp-image-1733" srcset="https://rodyne.com/wp-content/uploads/front-panel.jpg 961w, https://rodyne.com/wp-content/uploads/front-panel-300x210.jpg 300w, https://rodyne.com/wp-content/uploads/front-panel-768x539.jpg 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">Now you need to remember this is not a metal panel it is a 1.6mm thick fibre-glass one, it does the job, but it wont take much abuse like a metal one will and flexes a little when handling it.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="209" src="https://rodyne.com/wp-content/uploads/blinkenlights1-1-1024x209.jpg" alt="" class="wp-image-1710" srcset="https://rodyne.com/wp-content/uploads/blinkenlights1-1-1024x209.jpg 1024w, https://rodyne.com/wp-content/uploads/blinkenlights1-1-300x61.jpg 300w, https://rodyne.com/wp-content/uploads/blinkenlights1-1-768x156.jpg 768w, https://rodyne.com/wp-content/uploads/blinkenlights1-1-1536x313.jpg 1536w, https://rodyne.com/wp-content/uploads/blinkenlights1-1.jpg 1935w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">There were a few ways to do this. I thought at first of not using a microprocessor and having 16 74HC595 shift registers for each LED, but then I would have to move the smarts to my raspberry pi or whatever server I am actually using to clock the data in. Also if I didn&#8217;t use a micro, the display could not do any blingy stuff. So I looked in my parts bin seeing what crappy microprocessors I could get rid of.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="764" height="502" src="https://rodyne.com/wp-content/uploads/partsbin.jpg" alt="" class="wp-image-1711" srcset="https://rodyne.com/wp-content/uploads/partsbin.jpg 764w, https://rodyne.com/wp-content/uploads/partsbin-300x197.jpg 300w" sizes="auto, (max-width: 764px) 100vw, 764px" /></figure>



<p class="wp-block-paragraph">Yes, I remembered now, I had almost 200 really crappy PIC16F1782 chips, left over from an update for one of my clients when he decided to add more functionality to his controller and we had to unsolder a batch of them, and resolder in the &#8220;better&#8221; PIC16F1783 which had another 256 bytes of RAM. I bet most hobbyist&#8217;s have some version of this chip around before the STM32&#8217;s, RP2040&#8217;s and ESP&#8217;s took over the world.</p>



<p class="wp-block-paragraph">First problem there are not enough GPIO pins on the 28-pin PIC to drive 128 LED&#8217;s, plus an address line, plus a serial receive pin, minimum GPIO&#8217;s for that is 24 (16&#215;8 LED scan matrix)+4(address)+1(RXD) = 29. A simple solution is to add two 74HC595 shift registers to increase the outputs by 16 at the expense of 4 pins to control them. Also the shift registers are only 9 cents each installed so that is what I did. Of course an even simpler solution would have been to grab the 44-pin PICs from the same box but where&#8217;s the fun in that!</p>



<p class="wp-block-paragraph">Now the first design constraint is I cannot use any through-hole components as the holes will show though to the other side of the board where the silk screen is and spoil the effect. The second design constraint was power, luckily server racks are generally located in dark rooms or inside away from sunlight so no need to pump too many amps though the LED&#8217;s. Last design constraint was to be able to actually use the LED&#8217;s as real register displays and for that I needed a serial port so I could write real values to the registers, with 4 address switches I can have 16 panels (32U) and display up to 64 x 32-bit words. So when I finally get back to my retro computer it is going to look awesome!</p>



<p class="wp-block-paragraph">How does it work. Easy! Each board has a connector for 5V, GND and Serial TTL USART data receive. for the video I used an FT232RL adaptor to plug into one of the RPI USB ports, then each board has two of these connectors so I chained another three boards off it each with different addresses set. Without any serial data and after 30 seconds the board will go into a random (ish) pattern using the address setting of the board to make them slightly different. As soon as a serial data message is received the board will display the value received, the format of the message is AxRy=zzzz&lt;CR&gt;</p>



<p class="wp-block-paragraph">Where Ax = the board address A0, A1, A2.. -&gt; AF (F=15 in hexadecimal)</p>



<p class="wp-block-paragraph">Ry = Register on the board, there are 8 16-bit registers R0, R1, R2.. -&gt; RF </p>



<p class="wp-block-paragraph">zzzz is the 16-bit data in hexadecimal.</p>



<p class="wp-block-paragraph">and &lt;CR&gt; is the ASCII carriage return character (0x0d)</p>



<p class="wp-block-paragraph">So to set the LED&#8217;s on the second panel, first register to 0011 0100 1001 1101 you would send ASCII</p>



<p class="wp-block-paragraph">A2R0=349D&lt;CR&gt;</p>



<p class="wp-block-paragraph">The PIC simply stores this and outputs the bits to the shift register and activates the correct scan line. We do this a few thousand times a second so the eye cannot perceive it. There is also a timeout if no serial data is received we just begin some random adding to the registers. The better PICs have good internal oscillators and you can ditch the external crystal if you are happy at 9600 baud</p>



<p class="wp-block-paragraph">So I got five boards made for a test. All the components were assembled except for the PIC which I left blank so I could add mine, the initial plan was to put them for sale. (as that is what I was looking for and there were none I could find) But then I thought nahh, the cost of 5 panels manufactured and delivered to me in NZ was NZ$215 (US$119) so by the time I soldered on the PIC, uploaded the code and packed them off for shipping, and added a beer for my efforts, they might be NZ$80 each and just too expensive for your average Joe. </p>



<p class="wp-block-paragraph">Also, based on the non-existent sales figures of my books (which are awesome by-the-way, hint-hint!), you can probably tell that I don&#8217;t do marketing very well; So I thought, its XMAS, and the season of goodwill, so put it out for free and maybe somebody else can make a go of it. I did also route out an STM32 version of the PCB with more street appeal but I have enough bling already and did not want to fork out another $2oo nor write another program, but if you have an EasyEDA account the files are <img decoding="async" class="wp-image-1738" style="width: NaNpx;" src="https://rodyne.com/wp-content/uploads/BackupProjects_rodyne_personal_0_20250112-1.zip" alt=""/><a href="https://rodyne.com/wp-content/uploads/BackupProjects_rodyne_personal_0_20250112-1.zip">here</a><img decoding="async" class="wp-image-1737" style="width: NaNpx;" src="https://rodyne.com/wp-content/uploads/BackupProjects_rodyne_personal_0_20250112.zip" alt=""/></p>



<p class="wp-block-paragraph">If you really don&#8217;t have a friend who does electronics, and really want me to make a batch for your company&#8217;s boring data-centre though just get in touch, ditto if you have any valid questions..</p>



<p class="wp-block-paragraph">I have published the schematic/PCB and C code at oshwlab you can either use JLPCB to manufacture them yourself or download the CAD files and do it elsewhere. Remember you need to solder your own PIC in or add one to the BOM and get it assembled (I think the cheapest in stock at JLPCB is about $1.50) also you will need a PICKIT or similar to flash the code and an FT232 5V cable if you want to plug it via USB and send data to it. The 5V from the Pi GPIO pin is fine but remember the RPi UART is 3.3V not 5V. I have not tried powering it off 3.3V but it should work (if a bit dimmer).</p>



<p class="wp-block-paragraph"><a href="https://oshwlab.com/rodyne/new-project">https://oshwlab.com/rodyne/new-project</a></p>



<p class="wp-block-paragraph">All design files are public domain. Do what you will with them. (CAD/Gerber files <a href="https://rodyne.com/wp-content/uploads/DAS-BLINKENLIGHTS-2U.zip">here</a> if you wish to duplicate or fab somewhere else)</p>



<p class="wp-block-paragraph">The schematic for those interested is below, but better to use the oshwlab link above and the EasyEDA editor (note my first time using oshwlab so hoping it all works)</p>



<figure class="wp-block-image size-large"><a href="https://oshwlab.com/rodyne/new-project"><img loading="lazy" decoding="async" width="1024" height="726" src="https://rodyne.com/wp-content/uploads/Schematic_BLINKENLIGHTS-2U_2025-01-11-1024x726.png" alt="" class="wp-image-1715" srcset="https://rodyne.com/wp-content/uploads/Schematic_BLINKENLIGHTS-2U_2025-01-11-1024x726.png 1024w, https://rodyne.com/wp-content/uploads/Schematic_BLINKENLIGHTS-2U_2025-01-11-300x213.png 300w, https://rodyne.com/wp-content/uploads/Schematic_BLINKENLIGHTS-2U_2025-01-11-768x544.png 768w, https://rodyne.com/wp-content/uploads/Schematic_BLINKENLIGHTS-2U_2025-01-11-1536x1089.png 1536w, https://rodyne.com/wp-content/uploads/Schematic_BLINKENLIGHTS-2U_2025-01-11.png 1652w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<p class="wp-block-paragraph">Working C Code below for those in the know. Please don&#8217;t criticise, it had to fit into 256 bytes of RAM and 2K of ROM (which it easily does) and I have a love/hate relationship with C at the best of times. And yes I know I&#8217;m not supposed to call procedures from interrupts, I just noticed that, so feel free to fix if you must and add some better random patterns.</p>



<pre class="wp-block-code"><code>#include &lt;xc.h&gt;

/*
 * Rx data is 9600/8/N/1. 
 * Uses free MPLABX and XC8 compiler. Requires PICKIT cable to program the microcontroller 
 * If no data received within several seconds then begin random stuff 
 */

// Press the help icon left of the MPLABX dashboard and select configuration registers for pragma/config settings

#pragma config FOSC     = ECH     // Use external 11.0592Mhz OSC
#pragma config PLLEN    = OFF     // No PLL
#pragma config WDTE     = OFF     // Watchdog timer OFF if debugging
#pragma config BOREN    = OFF     // Turn on Brown-out reset
#pragma config BORV     = HI      // Brown out reset HI
#pragma config STVREN   = OFF     // Stack Overflow or Underflow will cause a Reset
#pragma config CLKOUTEN = OFF     // Clk output pin as GPIO

#define RESET_PIN(state)    LATAbits.LATA1 = state // Output - Shift Reg 74HC595 Reset (Active low)
#define LAT_PIN(state)      LATAbits.LATA3 = state // Output - Shift Reg 74HC595 output D-Latch clock
#define SERDAT_PIN(state)   LATAbits.LATA0 = state // Output - Shift Reg 74HC595 Serial In Data
#define SERCLK_PIN(state)   LATAbits.LATA2 = state // Output - Shift Reg 74HC595 Master In Data Clock
#define C1(state)           LATCbits.LATC4 = state // Output - Col Driver
#define C2(state)           LATCbits.LATC0 = state // Output - Col Driver
#define C3(state)           LATCbits.LATC5 = state // Output - Col Driver
#define C4(state)           LATCbits.LATC1 = state // Output - Col Driver
#define C5(state)           LATAbits.LATA5 = state // Output - Col Driver
#define C6(state)           LATCbits.LATC3 = state // Output - Col Driver
#define C7(state)           LATAbits.LATA6 = state // Output - Col Driver
#define C8(state)           LATCbits.LATC2 = state // Output - Col Driver

// Port B RB0-RB3 used as address control dip switch inputs rest N/U
// Port C7 UART RXD Input

volatile unsigned short LEDRegister&#91;8], mask, res, res2;

volatile unsigned char  HostActiveTimeout=0;
volatile unsigned char  RxBuffer&#91;21];
volatile unsigned char  RxBufPtr=0;
volatile unsigned char  RxDataRdy=0;
volatile unsigned char  addr=0;
volatile unsigned char  i, ColSel, RegisterSelect;

void USdelay(unsigned char a)
{
    while(a--) NOP();
}

void ClockData(void)
{
    SERCLK_PIN(0);
    NOP(); NOP(); NOP(); NOP(); 
    SERCLK_PIN(1);
    NOP(); NOP(); NOP(); NOP(); 
    SERCLK_PIN(0);
}

void ClockLatch(void)
{
    LAT_PIN(0);
    NOP(); NOP(); NOP(); NOP(); 
    LAT_PIN(1);
    NOP(); NOP(); NOP(); NOP(); 
    LAT_PIN(0);
}

void __interrupt() TimerAndUartInterrupt()
{    
    if(PIR1bits.RCIF)
    {
        if (RCSTAbits.FERR || RCSTAbits.OERR) // reset any errors
        {
          RCSTAbits.CREN = 0;
          RCSTAbits.CREN = 1;
        }  
        //read the byte from receive register into receive buffer
        if(RxBufPtr&lt;20)
        {  
          RxBuffer&#91;RxBufPtr++] = RCREG;
          if(RCREG==0x0d) RxDataRdy=1; // message received so decode
        }
        PIR1bits.RCIF=0; // re-enable interrupt for next char
    }   
    if(INTCONbits.T0IF) // clock Selected register into the 74HC595 shift register. ensure takes &lt; 740uS !!!
    {
        mask=0x8000;
        while(mask)
        {                
            res = LEDRegister&#91;RegisterSelect] &amp; mask;
            if(res==0) SERDAT_PIN(1); else SERDAT_PIN(0);
            ClockData();
            mask=mask&gt;&gt;1;
        }    
        ClockLatch();

        // enable the correct scan column we are displaying (Selected reg LED's ON) and disable all other cols so other LEDs OFF
        if(ColSel==0) C1(1); else C1(0);
        if(ColSel==1) C2(1); else C2(0);
        if(ColSel==2) C3(1); else C3(0);
        if(ColSel==3) C4(1); else C4(0);
        if(ColSel==4) C5(1); else C5(0);
        if(ColSel==5) C6(1); else C6(0);
        if(ColSel==6) C7(1); else C7(0);
        if(ColSel==7) C8(1); else C8(0);

        ColSel++;
        if(ColSel &gt; 7) ColSel=0; // Column select goes 0-7 (Note can make this bigger than 8 to DIM LED brightness)

        RegisterSelect++;
        if(RegisterSelect &gt; 7) RegisterSelect=0; // Register select goes 0-7 (Keep separate from ColSel in case we want brightness altered)
        INTCONbits.T0IF=0;
    }    
}

unsigned char ascii2dec( unsigned char data) // expecting a hexedecimal num 0..F anything else returns 0
{
    if(data=='0') return 0;
    if(data=='1') return 1;
    if(data=='2') return 2;
    if(data=='3') return 3;
    if(data=='4') return 4;
    if(data=='5') return 5;
    if(data=='6') return 6;
    if(data=='7') return 7;
    if(data=='8') return 8;
    if(data=='9') return 9;
    if(data=='A') return 10;
    if(data=='B') return 11;
    if(data=='C') return 12;
    if(data=='D') return 13;
    if(data=='E') return 14;
    if(data=='F') return 15;
    if(data=='a') return 10;
    if(data=='b') return 11;
    if(data=='c') return 12;
    if(data=='d') return 13;
    if(data=='e') return 14;
    if(data=='f') return 15;
    return 0; // default/error
}

int main()
{
    TRISA   = 0x00; // PortA: All Output
    ANSELA  = 0x00;
    LATA    = 0x00;
    TRISB   = 0x0f; // PortB: RB0-Rb3 input / rest output
    ANSELB  = 0x00;
    LATB    = 0x00;
    WPUB    = 0x0f; // pull up address bits pulled down if sw set
    TRISC   = 0x80; // PortC: All output except RC7 for RXD
    LATC    = 0x00;

    addr = (~PORTB) &amp; 0x0f; // dip switch returns 0-15 (HAVE PULL UP SO ON=0)
    RxDataRdy = 0;
    RegisterSelect = 0;
    ColSel = 0;
    HostActiveTimeout = 0;  
    for(i=0; i&lt;8; i++) LEDRegister&#91;i] = 0XC0F3&gt;&gt;addr;
    for(i=0; i&lt;20; i++) RxBuffer&#91;i]=0;
    
    RESET_PIN(1); // remove 74HC595D reset to allow data shifting
    ClockLatch();
    USdelay(200);

    // configure serial UART (connected to USB)  9600 baud 8/N/1 (Calculations based on OSC=11.0952Mhz P325 datasheet)
    TXSTAbits.BRGH = 0;
    TXSTAbits.SYNC= 0;  // Async
    BAUDCONbits.BRG16 = 0;
    SPBRG = 17;
    RC1STAbits.SPEN=1;  // Enable Serial
    TXSTAbits.TXEN= 0;  // TX DISABLED
    RCSTAbits.CREN= 1;  // RX ENABLED
    PIE1bits.RCIE = 1;  // enable receive interrupt
    INTCONbits.PEIE=1;  // enable interrupts for UART  

    // configure timer 0 interrupt: tick = Fosc (11.0592Mhz) /4/8/256 = approx every 740uS
    OPTION_REG = 0x02;
    INTCONbits.T0IE=1;

    // configure timer 1 (as a 16 bit counter, tick = Fosc/4/8 = 2.88uS
    T1CON  = 0x31;
    T1GCON = 0;
    TMR1L  = 0;
    TMR1H  = addr*16; // so multiple panels change at different times if all powered up together  

    INTCONbits.GIE =1;  // enable interrupts
    
    while(1)
    {
        CLRWDT();

        if(TMR1H&gt;250) // approx every 188mS update the serial data timeout and if no data received after 5 seconds then give a rand pattern
        {  
            TMR1H=0;
            if(HostActiveTimeout&lt;250)
                HostActiveTimeout++;
            else
            {
                // no data coming in so do something interesting
                for(i=0;i&lt;8;i++)                    
                  LEDRegister&#91;i]=LEDRegister&#91;i]+TMR0+addr+i;
            }    
        }
        if(RxDataRdy) // RXD decode 9 char message received through serial and set regLED&#91;n] (expecting format AnRn=0000) Where A=Panel Address, R=Register and n = 0..F eg "A0R4=1234"
        {
            if(RxBuffer&#91;0]=='A' &amp;&amp; ascii2dec(RxBuffer&#91;1])==addr &amp;&amp; RxBuffer&#91;2]=='R' &amp;&amp; RxBuffer&#91;4]=='=') // format still good
            {
                i=ascii2dec(RxBuffer&#91;3])-addr; // decode the reg number ASCII to dec
                if (i&gt;=0 &amp;&amp; i&lt;8)
                {    
                    res2 = (ascii2dec(RxBuffer&#91;5])*4096) + (ascii2dec(RxBuffer&#91;6])*256) + (ascii2dec(RxBuffer&#91;7])*16) + ascii2dec(RxBuffer&#91;8]);
                    LEDRegister&#91;i]=res2;
                }    
                HostActiveTimeout=0; // valid message, reset timeout
            }   
            RxDataRdy=0;
            RxBufPtr=0;
        }  
    }  
}
</code></pre>



<p class="wp-block-paragraph"><a href="https://news.ycombinator.com/item?id=42672366" target="_blank" rel="noreferrer noopener">Hacker news Comments</a></p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
