VIC-20 browsing the web: Live News

In this tutorial we will browse live news on the web with a VIC-20 connected to a cheap device acting as a web gateway. This project will probably work on C-64 as well.

In the “VIC-20 with Arduino” projects I presented past weeks as a series of tutorials, we have seen that a VIC-20 can perform a lot of things with the help of an external device such as Arduino, from the controlling of lights and sensors, up to the reading and display of WWW websites information. All we need is a simple cable whose building instructions were provided in tutorial #1.

Today we will use that cable together with a new device, an ESP8266 module, in order to browse a news web site. ESP8266 is a versatile little device that I bought for just a couple of Euros, some advice for buying will be provided at the end of the page in case you need it.

As usual I made a short video that you can find at the bottom of the page, so that you can quickly see what we are about to achieve with this tutorial.

In a few words, VIC-20 will browse a commercial web site (I picked C.B.S. News which is well known) and ESP8266 module will act as a gateway: it will connect to a local Wi-fi, access the web and send data to VIC-20 along the serial line (our cable). From our VIC-20 we will get the website’s list of news headlines by entering 0 (zero), and will read any of the news by entering its number, after that we can return to the initial menu by entering 0 again.

Important note

VIC-20 cannot handle the complexity of today’s web pages. Also, ESP8266 like Arduino has limited memory and string manipulation capabilities are limited too. So, in order to browse an ordinary web site with a VIC-20 the following two conditions must hold:

  • You have to choose the web site in advance. I have chosen C.B.S. News for this example.
  • We need a little PHP program that can serve as a proxy and that can fetch the desired web page and simplify it, by stripping away all that text that cannot be used by VIC-20 (headers, HTML tags and fancy gadgets), keeping only the necessary information that will be displayed. PHP script will also adjust word wrap so that the text gets easily readable on the VIC-20.

This way, the software running on the ESP8266 will be very simple and also VIC-20s part will be simple too. The solution presented here works well at the time I am writing (April 2025) but it depends on the structure of the target website’s web pages. If the site changes its page format, or if you want to browse news on a different site, then the PHP program must be adpted accordingly. I wil provide some hints about how to adapt it in case of need.

Given the above premise, let’s start.

Required hardware

  • ESP8266 module. There are several implementations based on the ESP8266 chip, mine was a NodeMCU (ESP-12E) 2.0 but there are others that should be equivalent.
  • VIC-20 user port serial cable (as built in project #1)
  • A physical VIC-20 (recommended 16K expansion)
  • A wi-fi network

At the bottom at the page you will find links to help you purchasing an ESP8266.

Building it

No breadboard is necessary. I simply used three female-to-female patch cables to plug the three cable wires onto ESP8266 male pins.

The connections (VIC-20 user port cable) are super simple:

  • White serial line: pin marked TX
  • Red serial line: pin marked RX
  • Ground of serial line (black): pin marked GND

At this point, it’s time to lauch the Arduino IDE and load the required software.

Arduino IDE

Right after connecting the module to my PC, it was reckognized and its driver was automatically installed for Windows 10 to communicate with it. If this does not happen, the hardware vendor provides some recommendation on drivers to be installed. In my case there was an indication printed on the back of the board, but it was not necessary for me to do anything at all. Googling “CP2102” as the instruction says brings up a number of downloadable drivers.

What you’ll actually need to do, is instruct Arduino IDE to load all ESP8266 specific libraries and stuff. In order to do this, click File–>Preferences, and scroll to the bottom of the dialog box. Where you find Additional boards manager, enter as a new line:

http://arduino.esp8266.com/stable/package_esp8266com_index.json

After a while you will get libraries and you will be able to select the correct board model from the board selection dropdown:

Mine was actually a NodeMCU 2.0 ESP-12E so I selected the closest name I found.

After this selection, a number of new specific ESP8266 sketches appear under the File–>Examples menu for you to experiment later, but for this project you simply get the software package from the link below wich contains the sketch to be used:

VIC-20 browses Live News with ESP8266

Unpack the ZIP, then move the folder Vic_to_8266_p6_Live_News” into your Arduino projects folder. Finally, open the “Vic_to_8266_p6_Live_News.ino” file from the Arduino IDE’s File–>Open menu. Below, you can see the code:

/**
   VIC20 to ESP8266 - Live News

   Browse internet Live News from a Commodore VIC-20

   vic20reloaded.com

*/

#include 

#include 
#include 

#include 

#include 

#include "arduino_secrets.h"

ESP8266WiFiMulti WiFiMulti;

String url;
String buf;

// Warning! User rate is limited, and news service can stop working
// If you want to use YOUR preferred news service you need to implement your own
// gateway to trim down text for the VIC-20
String gatewayserver="http://xixi.vic20reloaded.com/gw-news.php";

void setup() {

  Serial.begin(1200);
  // Serial.setDebugOutput(true);

  Serial.println();
  Serial.println();
  Serial.println();

  for (uint8_t t = 4; t > 0; t--) {
    Serial.printf("[SETUP] WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
  }

  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(SECRET_SSID, SECRET_PASS);
}

void loop() {

  // wait for WiFi connection
  if ((WiFiMulti.run() == WL_CONNECTED)) {

      // IF THE VIC20 HAS DATA THEN PRINT IT HERE
    if (Serial.available()) {
      buf=Serial.readStringUntil(13);
      if (buf.length()>0) {
        //Serial.print("go: "); Serial.println(buf);
        url=gatewayserver+"?q="+buf;

        WiFiClient client;

        HTTPClient http;

        //Serial.print("[HTTP] begin...\n");
        if (http.begin(client, url)) {  // HTTP


          //Serial.print("[HTTP] GET...\n");
          // start connection and send HTTP header
          int httpCode = http.GET();

          // httpCode will be negative on error
          if (httpCode > 0) {
            // HTTP header has been send and Server response header has been handled
            //Serial.printf("[HTTP] GET... code: %d\n", httpCode);

            // file found at server
            if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
              String payload = http.getString();
              Serial.print(payload);
              Serial.print('\r');
              Serial.print('\r');
              Serial.print('>');
            } else {
              Serial.printf("http error: %d\r", httpCode);
            }
          } else {
            Serial.printf("[HTTP] GET failed, error: %s\r", http.errorToString(httpCode).c_str());
          }

          http.end();
        } else {
          Serial.println("[HTTP] Unable to connect");
        }
      }
    }
  }

  delay(500);
}

// do NOT copy anything you find below this line
// -----------------------------------------------------------

As it stands, the code is very simple. There are a few points to be edited for a correct configuration:

  • file arduino_secrets.h: it contains two #defined strings that must match YOUR Wifi SSID and password, otherwise the ESP8266 will never connect to Internet. Edit it, and save it.
  • String gatewayserver at the beginning of the code: this one points to our service website xixi.vic20reloaded.com and need not to be changed at this time. The PHP code of the gw-news.php script is explained later: this one is the PHP script that actually fetches, parses and trims down the news website content. Please just be aware that if you want to use a different news website, or if the service turns out to be not working (the target news site is not controlled by me and might change formats and addresses in the future) then you should adapt the PHP script, and place it onto your own web hosting service. More on this later.

The code running inside the ESP8266 is generic and very simple.

It connects to WiFi inside the setup() function.

Then, inside the loop(), provided that the Wifi connection is up, it scans the serial input from the VIC-20. Everything received is sent to the PHP gateway script inside a “q” parameter (q=query). Then, what is received from the PHP gateway script is transmitted back to the VIC-20 as it is, plus a couple of carriage returns (“\r”) and the “>” prompt at the end.

Notice

If you’ve followed my previous tutorials based on Arduino, you wil probably expect that debug messages can be read over the serial line while the module is connected to the PC. Unlike Arduino, ESP8266 has just one bi-directional serial line and it uses connection at 1200 also dureing the phase of sketch upload/flashing. Therefore:

  1. While uploading the software from Arduino IDE to the ESP8266 module, the cable between ESP8266 and the VIC-20 must be disconnected. Otherwise the upload will not take place. You will reconnect the cable after successful upload.
  2. If you have the PC connected during dialogue between ESP8266 and VIC-20 do not use the serial monitor. If you do, you might see traffic of chars with VIC-20 but you might also interfere and prevent the dialogue to work correctly. Arduino IDE’s serial monitor will NOT be used in this project, at all.

VIC-20 software

At VIC-20’s side, we need nothing more that the good old and very simple TERM1200 program that we used in tutorial project number 1 and that I provided in the vic20-network.d64 diskette, but you can also type it via keyboard:

10 OPEN2,2,0,CHR$(8)+CHR$(0)
20 GET#2,A$:PRINTA$;
30 GETA$:PRINTA$;:IFA$<>""THENS$=S$+A$
40 IFA$=CHR$(13)THENPRINT#2,S$;:S$=""
50 GOTO20

please remember when running it:

  • We need to use the Upper/Lowercase charset, so press C= and SHIFT on the VIC keyboard to do the shift.
  • Commands to be entered for operation are very simple:

0 = menu,

1= selects the desired news item

  • Press RUN-STOP to stop the program.

PHP gateway script

You should have understood by now that most of the key logic is performed by the PHP gateway script. It might sound like cheating, but honestly speaking, neither the VIC-20 nor the ESP8266 have got the capability to effectively process hundreds of Kbytes of string data. I find the string manipulation primitives of the Arduino language a pain to work with, while I like the power of PHP in this field.

Last but not least, it seems ESP8266 has a hard time when dealing with HTTPS sites so leaving the task of managing HTTPS connection to a PHP script running on a simple HTTP server seemed to me a good solution.

Here is the gw-news.php script:

\n";	// Use this value when testing via web
$MAXSIZE=410;
$MAXTITLES=6;

define('MAGPIE_OUTPUT_ENCODING', 'ISO-8859-1'); // Configure for parsing
require_once('magpie/rss_fetch.inc');

header('Content-Type: text/html; charset=iso-8859-1');

logtofile("Startup");
if (isset($_GET['q'])) {
    $q=preg_replace("/[^a-z\d\-]/", '', str_replace(' ','-',strtolower(trim($_GET['q']))));
}
$out='';
if (maxrequests()) { // Have we reached MAX requests?
    $out="tOO many requests$n";
} else {
    
    $url="https://www.cbsnews.com/latest/rss/main";
    $rss = fetch_rss($url);

    $i=1;
    $menu = chr(156).$rss->channel['title'].chr(31)."$n";
    $news=array();
    $descr=array();
    $links=array();
    foreach ($rss->items as $item) {
            $title = $item['title'];
            $desc = $item['description'];
            $url   = $item['link'];
            //$content .= "
  • $title
    $desc

  • "; $news[$i]=$title; $descr[$i]=$desc; $links[$i]=$item['link']; if ($i<=$MAXTITLES) { if (isset($_GET['x'])) { $menu .="$i. $title$n$desc$n"; } else { $odd=($i%2) != 0; $menu .=($odd?chr(144):'')."$i. $title".($odd?chr(31):'')."$n"; } } $i++; } switch ($c=intval($q)) { case 0; $out.=$menu; break; default: $out.=$news[$c]."$n"; if ($_GET['l']) echo "{$links[$c]}$n"; $error=null; try { $response = $httpClient->get($links[$c], [ 'headers' => [ // Pretending we're Chrome on Android 'User-Agent' => 'Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.58 Mobile Safari/537.36', ] ]); $htmlString = (string) $response->getBody(); } catch (GuzzleHttp\Exception\ClientException $e) { $response = $e->getResponse(); $responseBodyAsString = $response->getBody()->getContents(); $error=$response; } if (!$error) { // HTML is often wonky, this suppresses a lot of warnings libxml_use_internal_errors(true); $doc = new DOMDocument(); $doc->loadHTML($htmlString); $xpath = new DOMXPath($doc); $items = $xpath->query("//section[@class='content__body']/p"); foreach ($items as $it) { $out.=$it->nodeValue."$n"; //$html.="
  • ".htmlspecialchars($it->nodeValue)."
  • $n"; } } else { $out.="Gateway error for {$links[$c]}: $error".date("Y-m-d h:i:s").$n; logtofile("Gateway error: $error\n".print_r($res,1),"$APP.log"); } } } sendout($out); exit; function sendout($out) { global $MAXSIZE; $out=cookForVic($out); if (strlen($out)>$MAXSIZE) { $out=substr($out,0,$MAXSIZE).'...(cont.)'.chr(31); // CHR31 Force blue } echo($out); } function cookForVic($out) { logtofile("Full text would be long ".strlen($out).":\n".$out); $cooked=chr(144); $col=0; $line=''; $firstline=true; $len=strlen($out); for ($i=0;$i<$len;$i++) { $c=$out[$i]; $asc=ord($c); logtofile("$c - $asc"); if ($asc==13) { if ($firstline) { $cooked.=$line.chr(31)."\r\r"; $firstline=false; } else { $cooked.=$line."\r\r"; } $col=0; $line=''; } else if (in_array($asc,array(194))) { // Skip char } else { // Normal char if ($asc==160) $c=' '; $col+=1; if ($col>21) { // Word Wrapping $x=strrpos($line,' '); // Uncomment the next to see char codes on the log //logtofile("line: $line col: $col - Space found at $x"); if ($x) { $cooked .= substr($line,0,$x)."\r"; $line = substr($line,$x+1); $col=strlen($line); } } if (($asc>=97) && ($asc<=123)) { $c=chr($asc-32); } if (($asc>=65) && ($asc<=90)) { $c=chr($asc+32); } $line.=$c; } } $cooked .= $line; return chr(147).$cooked; // CHR$(147)=CLEAR SCREEN // CHR 31 force blue } function logtofile($what) { global $APP; // APP name // write to log file $fp = fopen("$APP.log", "a+"); // append to end of content in log file if ($fp) { //chmod($mailer_logfile, 0777); // make sure file is writable $res = fwrite($fp, date("[Y-m-d H:i:s] ") . $what."\n"); fclose($fp); return TRUE; } else { echo "Cannot write log: $logfile"; return FALSE; } } // Has current client reached max number of requests? // This function can be modified to return TRUE // function maxrequests() { global $_SERVER; // Your own implementation here return false; }

    This PHP script uses the MAGPIE library to fetch and parse a website’s RSS feed, the news items titls will be used as the news menu. From then on, it uses links included in the website’s feed to target individual news items. Let’s see how it works.

    Line 4: since this php script uses the guzzlehttp package for fetching individual news items, it includes the standard vendor/autoload.php script. This one has been created on the hosting server by using the following command:

     composer require guzzlehttp/guzzle:7.3

    if you are not familiar with Composer (it is a sort of automated system to get useful libraries of code), you may want to take a look at this website.

    Line 10: $MAXSIZE defines how many chars can be sent towards the VIC-20 before incurring in the buffer overrun.

    Line 11: $MAXTITLES defines how may titles you want to display from the news feed. 5 or 6 is a good choice, since VIC-20 displays 22 just text lines.

    Line 20; Gets the command from the “q” parameter. The command should be an integer number from zero to $MAXTITLES. We perform some clean up in order to avoid mis-uses.

    Line 23: In order to avoid abuses, I use a function named maxrequests() to decide whether the requesting client has reached the max number of requests I allow. In this version of the script there is no limit, but in my own, real implementation the maxrequests() function will block clients after 40 requests from the same IP number.

    Line 27: Find here the URL of the news feed. If you want to use a different news service, replace this one URL with your desired RSS feed news.

    Line 31-53: Depending on the news feed, it builds the menu to be displayed on the VIC-20 in case user chooses option ‘0’. Notice how I used chr(156) and chr(31) to change print colour. Check out VIC-20 ASCII codes for reference.

    Line 59-73: Upon choice of the user, fetches desired news page by using GuzzleHttp.

    Line 87-95: Extracts from fetched page the text of the news. Here I used XPATH for getting the correct HTML object, which is a section whose class is named “content__body”. If you change news service, you will have to study the new page structure and learn how to target specific classes by using XPATH methods.

    Line 125: Function cookForVic() – formats text so that word wrap is rendered for better readability.

    In this PHP script I used plenty of CHR codes to set up proper colors on the VIC display.

    Conclusions

    This project uses a real web site and I think it’s a good example because I do not control in any way. Just to be on the safe side, I had to provide an extra layer of logic by using an http script whose task is to fetch an HTTPS (notice the final “s”) page, and grab the content, trim it down and prepare it for visualisation on VIC-20.

    This approach turns out to be very fast and flexible so, in case you want to change news service, you just need to adapt the PHP script.

    I was positively impressed by the speed. After entering the choice at the menu (see the video below), the reaction time given by the ESP8266 + PHP script is short and I can read news with no particular lag, just as if I used an ordinary PC with a browser.

    In order to avoid buffer overruns (the phenomena that occurs when too much text is sent to the VIC serial line, whose buffer size is limited to 256 chars in each direction) I had to trim the news text down to about 410 chars. Which is OK as a first approach, because it gives enough text to read and it does not complicate things. A later upgrade of this project could be to implement some sync mechanism to allow the VIC to request a press of a key after reading the first part of a news, and to request+display the next parts of the page as a subsequent steps (imagine a –more– prompt), until the whole news text has been displayed.

    I made a short clip on YouTube in order to let you give a glance at how it works.

    Buying advice

    Feel free to leave a comment or a rating below, in case of interest I might make more projects about connecting VIC-20 the Internet.


    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading...

    Leave a Reply

    Your email address will not be published. Required fields are marked *