{"id":21368127,"url":"https://github.com/robvanlopik/pots","last_synced_at":"2025-06-20T01:06:24.679Z","repository":{"id":47217047,"uuid":"307127353","full_name":"robvanlopik/Pots","owner":"robvanlopik","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-01T14:58:37.000Z","size":416,"stargazers_count":7,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-01T23:55:20.564Z","etag":null,"topics":["arduino","electronics","pharo","raspberry-pi"],"latest_commit_sha":null,"homepage":"","language":"Smalltalk","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robvanlopik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-10-25T15:18:38.000Z","updated_at":"2025-06-01T14:58:40.000Z","dependencies_parsed_at":"2025-06-01T15:42:45.121Z","dependency_job_id":"b777f629-74bc-44b4-b83d-f5a37634cc90","html_url":"https://github.com/robvanlopik/Pots","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/robvanlopik/Pots","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robvanlopik%2FPots","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robvanlopik%2FPots/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robvanlopik%2FPots/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robvanlopik%2FPots/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robvanlopik","download_url":"https://codeload.github.com/robvanlopik/Pots/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robvanlopik%2FPots/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260857365,"owners_count":23073435,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","electronics","pharo","raspberry-pi"],"created_at":"2024-11-22T07:22:57.156Z","updated_at":"2025-06-20T01:06:19.663Z","avatar_url":"https://github.com/robvanlopik.png","language":"Smalltalk","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n# Pots - Pharo-of-Things Simplified (DRAFT)\n\n## Attribution\n\n*This project is indebted to the Pharo-of-Things project (https://github.com/pharo-iot/PharoThings)  for ideas, modelling and code. Especially the pin and device model stem from Pot. It misses the remote capabilities of Telepharo. On the other hand it fully supports digital I/O, PWM and servo functions on  Raspberry Pi, Pico, Arduino and ESP32 devices. The design considerations are documented in Pot-to-Pots.md in this repo. In this readme no previous knowledge of PharoThings is presupposed. If you are acquainted with Pharo-of-Things you should know that the term `Board` has been replaced by `Controller` and `Mode` and `Function` coalesced into `Role`.*\n\n## Main classes and relations between them\n\n### Controller\n\nA controller (class `PotsController`) is a concept that stands for a physical device like a Raspberry Pi, an Arduino microprocessor or even a 16-port Led driver like the PCA9685. A controller is brought to life by a driver (`PotsDriver`) that talks to the real hardware. A controller contains pins (`PotsPin`) which are the objects we manipulate for setting or retrieving the values of the physical device.\n\n### Driver\n\nThe driver has two functions:\n\n1. Communicate with the physical device. This can be through an existing driver (like PiGPIO for the Raspberry Pi or Firmata for an Arduino, PicodDriver for thee Raspberry Pico), or ESP32Driver for the ESP32 or through a device (`PotsDevice`) that has been defined for an existing controller (see under \"Device\"). We call this lower level driver the \"base driver\". The PotsDriver presents a common API to the Controller.\n2. Provide the controller with a list of all pins with their ID's and their capabilities (possible roles). A controller is completely defined by its driver, so there will usually be no need to subclass `PotsController`.\n\n#### Example code\n\nThe following code fragments would create four controllers, one for a Pi (with pigpiod running on the Pi), the second for an Arduino (loaded with a Firmata sketch), the third for a Raspberry Pico (with Picod daemon) and the last for an ESP32 running my own deaemon.toit :\n\n```smalltalk\nmyPi := PotsController new driver: (PotsPiGPIODriver onIP: '192.168.1.92' port: 8888).\nmyArduino := PotsController new driver: (PotsFirmataDriver onPort: 'COM3' baudRate: 57600).\nmyPico := PotsController new driver: (PotsPicodDriver onPort: '/dev/ttyACM0').\nmyESP32 := PotsController new driver: (PotsESP32Driver brokerIP: 'mqtt://test.mosquitto.org' deviceName: 'test1').\n```\nIf you are running this on the Raspberry Pi itself, you should use the localhost address 127.0.0.1.\n\n### Pin\n\nA controller has a number of pins (else it wouldn't be of much use). Pins are designated by their native Id's. Pins can have alternate id's; these are used by the Firmata and Picod driver to denote the analog pins (that can also be digital i/o pins, or i2c) and for the Pi you could use them for the WiringPi numbering scheme. \n\nPins respond to the messages `value` or `value:`.  The result or parameter depends on the role the pins play. \n\nRoles are set by the messages `beDigitalInput`, `beDigitalOutput`, `beAnalogInput`, beAnalogOutput, `bePWMoutput` or `beServoOutput`.  \n\nValues are returned and provided in meaningful units: 0/1 for digital pins, volts for analog i/o, percentage for PWM and degrees (0 - 180) for servo pins. If you know the internals, you can use `rawValue`.\n\n### Role\n\nRole (`PotsRole`) is a central concept. A role captures what  a pin can do, its capabilities. A pin has one current role (`currentRole`)  and a number of possible roles. The `beSomething` methods change the current role, after checking this role is available for that pin. The (current) role can also contain relevant parameters for that role, like the resolution for an analog input, or the minimum and maximum pulse width for a servo output. Until now we have only seen solo roles (`PotsSoloRole`). When a pin cooperates with other pins to perform a function, it has an ensemble role (see Device) and would normally not be individually addressed.\n\n#### Example code\n\nLet's start with a LED. Most Arduinos have a LED on pin 13 en on the Pico the internal LED is on GPIO 25; for the Pi you will have to connect a LED to  GPIO pin 13 (that is 33 on the connector), or simply use a volt meter. On an ESP32 there is often a LED on pin 2. We use `myController` instead of `myPi` or `myArduino`.\n\n```smalltalk\nled := myController pinWithId: 13. \"or 25 for a Pico or 2 for ESP32\"\nled beDigitalOutput.\nled value: 1.\nled toggle.\nled bePWMOutput. \"This raises an error on an Arduino Uno,because pin 13 doesn't do PWM\"\nled value: 50\nled incrementValueBy: 20.\n```\n\nFor the Arduino you must connect a LED to a PWM capable pin. \n\nThe following is only possible on Arduino, Pico:\n\n```smalltalk\na0 := myArduino pinWithAltId: 0. \"On an Arduino Uno this is pin 14, on the Pico it is gpio26\"\na0 beAnalogInput. \"note that analog pins can also do digital i/o\"\na0 enableReporting. \"This is specific to Firmata, not necessary on Pico\"\nmeasurement := a0 value. \"The result is in Volts\"\n```\nThe ESP32 has no altId's defined so for analog input you can use pins 32, 33, 34, 35, 39 sand for analog output pins 25 and 26.\n#### Inspector and PotsLayout\nWhen you inspect an instance of PotsController, you get a list of the pins in numerical order. Each line has the following informatio: pin number, alternative pin number, current role, last value and permitted roles (named capabilities). To make this look more like the actual board or connector you can apply a PotsLayout to the controller like\n```smalltalk\nmyArduino installLayout: : PotsLayout forArduinoUno.\n```\nThere are definitions for the Pi3, Pico, ESP32-30pins and the Uno. The pins are now arranged in two columns with their details on the left and the right. From the code it will be evident how to make your own layout\n\n### Devices\n\nA `PotsDevice` is a software construct that simulates a real device. It uses one or more pins of the controller. These pins can be manipulated directly by the device code, or they can be used by a specific protocol (I2C only, at present), depending of the capabilities of the base drivers\n\nA `PotsDevice` is instantiated with the method `installDevice: aDevice`, where `aDevice` is an instance of a `PotsDevice` that does not have to be working, but must know which pins to use. The pins are then \"claimed\" so they cannot be used by other devices.\n\nA simple  example is the `PotsRGBLedDevice` that uses three pins to drive an RGB LED. It's only important method is `color:` that takes an instance of `Color` as argument. More complex is the `PotsHD44780Device` that drives the popular (and cheap) 16x2 Led display. It uses 6 pins: 4 for data and 2 for control. \n\nDevices that use inputs need something extra: either they should be made aware of a change in input (in Pharo that would be an Announcement) or the device code itself should contain a polling loop.  Firmata Picod and PiGPIO drivers support announcements of changes in digital inputs (essentially because the polling loop already has been implemented in the driver, or in the daemon code on the physical device). \n\n#### I2C devices\n\nMany microprocessors have special hardware to support the I2C protocol. This is also true for the Raspberry Pi, Pico and Arduino-like boards. The protocol uses two pins of the controller (named SDA and SCL) and is supported by all three baseDrivers, PiGPIO, Picod and Firmata. The drivers know which pins to use and set these pins automatically to the `PotsI2CRole`.  The `PotsI2CDevice` manages the I2C communication and provides the functionality of the device in a useful way. For example,the `PotsDS1307Device` exposes the real time clock with messages like `dateAndTime` or `dayOfWeek`. Another example is `PotsBME280Device` that exposes the temperature, pressure and humidity sensor BME280 with messages like `readTemperature` and `readPressure1` Note that the code of these devices is almost identical to the code in Pharo-of-Things.\n\n### Controllers on devices\nAmong the I2C devices we have  the `PotsPCA9685Device` and the `PotsPCF8574Device`. Both have pins that function as inputs or outputs, so the question arises what makes them different from a `Controller`? In fact, nothing, once you construct an appropriate driver. `PotsDriver` has two subclasses, the `PotsDriverDriver` and the `PotsDeviceDriver`. Up till now we used the first,  a driver that uses another driver. The second is a driver that uses a `PotsDevice`. A `PotsDeviceDriver` needs a working instance of the appropriate `PotsDevice`to start with. Using an installed 8-bits i2C-IO-extender like `PotsPCF8574` that is often used together with a standard 16x2 LCD (and even sold as one unit) you can control the `PotsHD44780Device` just like we did above.\n\n#### Example code\nFirst we look at the PCF8574 as a device. The chip has 8 pins that can be set to logical 1 or 0. When you write a 1 to a pin, you ran read it back, resulting in 1 if the pin is free or 0 when it is pulled down to ground. Actually, the pins cannot be addressed individually, but only all together. So, either you send a byte, or you read one. Often the chip is mounted with some additional circuitry, ready to drive an LCD display. In that case pin 3 is meant to drive the LCD backlight and is connected to the base of a npn-transistor, effectively pulling it to ground.\n\n```smalltalk\nard := PotsController new driver: (PotsFirmataDriver onPort: 'COM3' baudRate: 57600).\npfc := ard installdevice: PotsPCF8574Device new. \"create the device\"\npfc writeByte: 2r10101010. \"check the output with a voltmeter\"\npfc writeByte: 16rFF. \"set all pins to 1 so you can read back their state\"\nstate := pfc readByte. \"this would show   (16r..) as pin 3 was pulled down\"\n```\n## To Do\nNo project is ever really finished. In this case there are practical to-do's like cleaning up (I worked on this stuff on and off for the last two years so many inconsistencies have crept in), adding devices, error handling, possibly adding controller devices. And there definitely must be more  tests, although a lot of functions depend on the physical device and its firmware. Maybe it makes sense to extend the announcement mechanisms of Firmata, PigPIO and Picod to the Pots \nframework as a whole.  \n\nSmall to-do's:\n- pullup and pull down resisters on digital outputs; not difficult, but different for different types of controllers. Essentially, \nthis is about the electrical behaviour of an output, so we would also like to capture the \"totem pole\" output of the PCA9685\n- Users will centainly find many more\n\n## Loading\nIn a playground execute\n```smalltalk\nMetacello new\n    baseline: 'Pots';\n    repository: 'github://robvanlopik/Pots:main';\n    load.\n```\nThis will also load the four drivers (PiGPIO, Picod, Firmata and ESP32). Also loaded is my fork of an FFI-based SerialPort driver by Pablo Tesone and the MQTT package by Sven van Caekenberghe.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobvanlopik%2Fpots","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobvanlopik%2Fpots","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobvanlopik%2Fpots/lists"}