├── .gitignore ├── LICENSE.md ├── README.md ├── bmp2lcd ├── generateHexArray.js └── index.html ├── examples ├── oled-exp.py ├── onion-i2c.py ├── pwm-exp.py ├── relay-exp.py └── sierpenski-1306.py ├── include ├── oled-exp.h ├── onion-debug.h ├── onion-i2c.h ├── onion-mcp23008-driver.h ├── pwm-exp.h └── relay-exp.h ├── makefile ├── rpcd └── i2c-exp.sh ├── src ├── lib │ ├── onion-debug.c │ ├── onion-i2c.c │ └── onion-mcp23008-driver.c ├── main-oled-exp.c ├── main-pwm-exp.c ├── main-relay-exp.c ├── oled-exp.c ├── pwm-exp.c ├── python │ ├── oled-exp-module.c │ ├── omegaMotors.py │ ├── onion-i2c-module.c │ ├── pwm-exp-module.c │ └── relay-exp-module.c └── relay-exp.c ├── test └── servo_exp_test.sh ├── tools └── remote_compile.sh └── upload.sh /.gitignore: -------------------------------------------------------------------------------- 1 | test/* 2 | tmp/* 3 | tmp* 4 | 5 | src/ubus-pwm-exp.c 6 | 7 | src/python/onionI2C.py 8 | 9 | common_commands.txt 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # i2c-exp-driver 2 | Project for drivers to program Omega Expansions that are I2C-based. 3 | 4 | # ubus Integration 5 | The drivers for the I2C-based Onion Expansions have been integrated with the ubus service via RPCD. They can be accessed through the i2c_exp service. 6 | 7 | 8 | [//]: # (pwm-exp) 9 | 10 | ## pwm-exp ubus Commands 11 | The Servo Expansion can be programmed using the `pwm-exp` command in the i2c_exp ubus service: 12 | ```bash 13 | ubus call i2c_exp pwm-exp '{"command":"String", "params": {"key": "value" } }' 14 | ``` 15 | 16 | The following commands are available: 17 | * init 18 | * sleep 19 | * set 20 | * set-period 21 | 22 | 23 | [//]: # (pwm-exp: init) 24 | 25 | ### Init Command 26 | The `init` command will reset the chip on the Servo Expansion and enable the oscillator. 27 | ```bash 28 | ubus call i2c_exp pwm-exp '{"command":"init"}' 29 | ``` 30 | 31 | [//]: # (pwm-exp: sleep) 32 | 33 | ### Sleep Command 34 | The `sleep` command will put the oscillator into sleep mode, disabling all PWM signals 35 | ```bash 36 | ubus call i2c_exp pwm-exp '{"command":"sleep"}' 37 | ``` 38 | 39 | [//]: # (pwm-exp: set) 40 | 41 | ### Set Command 42 | The `set` command is used to generate a PWM signal on a specific channel based on a **duty cycle percentage**: 43 | ```bash 44 | ubus call i2c_exp pwm-exp '{"command":"set", "params":{"channel":"", "duty":""} }' 45 | ``` 46 | 47 | The CHANNEL can be: 48 | * 0 - 15 49 | * control an individual channel 50 | * all 51 | * control all channels at once 52 | 53 | The DUTY CYCLE can be an integer or **floating point** number between 0 and 100 54 | 55 | Optional parameters: 56 | * `"frequency":""` 57 | * Sets the PWM signal frequency 58 | * If not specified, default of 50 Hz is used 59 | * Frequency range is 24 Hz to 1526 Hz 60 | * `"delay":""` 61 | * Adds a delay in the PWM signal, can be an integer or **floating point** number between 0 and 100 62 | * 0% delay by default 63 | 64 | 65 | [//]: # (pwm-exp: set-period) 66 | 67 | ### Set-Period Command 68 | The `set-period` command is used to generate a PWM signal on a specific channel based on a **PWM period and pulse width** (both can be integer or floating point numbers): 69 | ```bash 70 | ubus call i2c_exp pwm-exp '{"command":"set-period", "params":{"channel":9, "pulse":"1.5", "period":20}}' 71 | ``` 72 | 73 | 74 | 75 | [//]: # (relay-exp) 76 | 77 | ## relay-exp ubus Commands 78 | The Relay Expansion can be programmed using the `relay-exp` command in the i2c_exp ubus service: 79 | 80 | 81 | [//]: # (relay-exp: set) 82 | 83 | ### Set Command 84 | The `set` command allows for programming the relay state: 85 | ```bash 86 | ubus call i2c_exp relay-exp '{"command":"set", "params":{"channel":"", "state":"RELAY STATE", "address":"
"}}' 87 | ``` 88 | 89 | The CHANNEL can be one of: 90 | * 0 - controls Relay0 91 | * 1 - controls Relay1 92 | * all - controls both relays 93 | 94 | The RELAY STATE can be one of: 95 | * off - relay is switched off 96 | * on - relay is switched on 97 | 98 | The ADDRESS argument can be either: 99 | * The DIP-SWITCH STATES 100 | * The Hex I2C device address 101 | 102 | #### Dip Switch States 103 | The DIP-SWITCH STATES should reflect the dip switch settings in binary starting with switch 1, then switch 2, then switch 3. The 0 position is when the switch is close the numbers. 104 | 105 | Not required when all switches are 0. 106 | 107 | Examples: 108 | * Switches 1 and 2 are 0 (close to the printed numbers), switch 3 is 1: 109 | * `"address":"001"` 110 | * Switches 1 and 3 are 1, switch 2 is 0: 111 | * `"address":"101"` 112 | * All switches are 1: 113 | * `"address":"111"` 114 | 115 | #### I2C Device Address 116 | The I2C Device Address should be just the hex address. 117 | 118 | For example: 119 | * 0x27 120 | * 0x23 121 | 122 | 123 | [//]: # (relay-exp: get) 124 | 125 | ### Get Command 126 | The `get` command implements reading the current state of a relay: 127 | ```bash 128 | ubus call i2c_exp relay-exp '{"command":"get", "params":{"channel":"", "address":"
"}}' 129 | ``` 130 | 131 | The CHANNEL can be one of: 132 | * 0 - controls Relay0 133 | * 1 - controls Relay1 134 | 135 | The ADDRESS argument can be either: 136 | * The DIP-SWITCH STATES 137 | * The Hex I2C device address 138 | 139 | #### Dip Switch States 140 | The DIP-SWITCH STATES should reflect the dip switch settings in binary starting with switch 1, then switch 2, then switch 3. The 0 position is when the switch is close the numbers. 141 | 142 | Not required when all switches are 0. 143 | 144 | Examples: 145 | * Switches 1 and 2 are 0 (close to the printed numbers), switch 3 is 1: 146 | * `"address":"001"` 147 | * Switches 1 and 3 are 1, switch 2 is 0: 148 | * `"address":"101"` 149 | * All switches are 1: 150 | * `"address":"111"` 151 | 152 | #### I2C Device Address 153 | The I2C Device Address should be just the hex address. 154 | 155 | For example: 156 | * 0x27 157 | * 0x23 158 | 159 | ### Return Values 160 | The ubus call will return different values based on if the command was successful or not. 161 | 162 | Successful Command: 163 | ```bash 164 | {"status":"success", "channel":", "state":""} 165 | ``` 166 | 167 | Unsuccessful Command: 168 | ```bash 169 | {"status":"success", "error":""} 170 | ``` 171 | 172 | 173 | [//]: # (oled-exp) 174 | 175 | ## oled-exp ubus Commands 176 | The OLED Expansion can be programmed using the `oled-exp` command in the i2c_exp ubus service: 177 | 178 | ### Set Command 179 | The only command is the `set` command, it allows the relays to be programmed: 180 | ```bash 181 | ubus call i2c_exp relay-exp '{"command":"set", "option":"opt" "params":{"":"}}' 182 | ``` 183 | 184 | [//]: # (oled-exp: options) 185 | 186 | #### Options 187 | Available options are 188 | * i 189 | * Initialize the display, **this must be done at powerup** 190 | * c 191 | * Clear the screen and set the cursor to the top left" 192 | 193 | Example Usage: 194 | * Initialize the display 195 | * `ubus call i2c_exp oled-exp '{"command":"set", "option":"i"}}'` 196 | * Clear the display and then write Onion Omega to it 197 | * `ubus call i2c_exp oled-exp '{"command":"set", "option":"c", "params":{"write":"Onion Omega"}}'` 198 | 199 | 200 | [//]: # (oled-exp: commands) 201 | 202 | #### Commands 203 | The following are the commands and their parameters as accepted by the oled-exp: 204 | * `power ` 205 | * `invert ` 206 | * `dim ` 207 | * `cursor ,` 208 | * `write ` 209 | * `scroll ` 210 | * `draw ` 211 | 212 | Commands can be strung together! 213 | 214 | [//]: # (oled-exp: commands: power) 215 | 216 | ##### Power 217 | Turn the display on or off. Can be used to toggle the display after it has been initialized. 218 | ``` 219 | "params":{"power":""} 220 | ``` 221 | 222 | 223 | [//]: # (oled-exp: commands: invert) 224 | 225 | ##### Invert 226 | Invert black and white on the display. Setting to `on` will enable the invert, setting to `off` will disable the inversion. 227 | ``` 228 | "params":{"invert":""} 229 | ``` 230 | 231 | 232 | [//]: # (oled-exp: commands: dim) 233 | 234 | ##### Dim 235 | Enable dimming the display. Setting to `on` will dim the display, setting to `off` will restore the default brightness. 236 | ``` 237 | "params":{"dim":""} 238 | ``` 239 | 240 | 241 | [//]: # (oled-exp: commands: cursor) 242 | 243 | ##### Cursor 244 | Set the cursor position on the display. 245 | 246 | The `row` parameter represents each character row (8 pixel rows) on the display, so the range is **0 to 7** 247 | 248 | The `column` parameter represents each character column, the range is **0 to 20** 249 | ``` 250 | "params":{"cursor":","} 251 | ``` 252 | 253 | ###### Examples 254 | Set the cursor to the start of the last character row: 255 | ``` 256 | "params":{"cursor":"7,0"} 257 | ``` 258 | 259 | Set the cursor to the middle of the 4th character row: 260 | ``` 261 | "params":{"cursor":"3,10"} 262 | ``` 263 | 264 | Set the cursor to the start of the 2nd character row: 265 | ``` 266 | "params":{"cursor":"1,0"} 267 | ``` 268 | 269 | Set the cursor to the top left (home position): 270 | ``` 271 | "params":{"cursor":"0,0"} 272 | ``` 273 | 274 | 275 | [//]: # (oled-exp: commands: write) 276 | 277 | ##### Write 278 | Write a string to the display. 279 | 280 | ``` 281 | "params":{"write":""} 282 | ``` 283 | 284 | ###### Notes 285 | To get a newline on the display, need to write `\\\\\n` in the message. 286 | 287 | For now, avoid the following characters: 288 | * `"` 289 | * `'` 290 | 291 | 292 | ###### Examples 293 | Write `Onion Omega` to the display: 294 | ``` 295 | "params":{"write":"Onion Omega"} 296 | ``` 297 | 298 | Write `Onion Corporation` and then the office address on the lines below: 299 | ``` 300 | "params":{"write":"Onion Corporation\\\\\n187 Denison St\\\\\nMarkham, ON\\\\\nCanada\\\\\nL3R-1B5"} 301 | ``` 302 | 303 | 304 | [//]: # (oled-exp: commands: scroll) 305 | 306 | ##### Scroll 307 | Enable scrolling of whatever is currently on the display. 308 | 309 | ``` 310 | "params":{"scroll":""} 311 | ``` 312 | 313 | ###### Directions 314 | Available directions: 315 | * left 316 | * right 317 | * diagonal-left 318 | * diagonal-right 319 | * stop 320 | * To disable scrolling 321 | 322 | 323 | [//]: # (oled-exp: commands: draw) 324 | 325 | ##### Draw 326 | Display an image from an LCD file on the display. 327 | 328 | *Details on LCD file: The LCD file must be generated by the Onion utility. For a 128x64 image, there should be 1024 bytes. Each byte is one character column (8 vertical pixels), with the LSB representing the top-most pixel.* 329 | 330 | ``` 331 | "params":{"draw":""} 332 | ``` 333 | 334 | 335 | -------------------------------------------------------------------------------- /bmp2lcd/generateHexArray.js: -------------------------------------------------------------------------------- 1 | 2 | var inputElement = document.getElementById("input"); 3 | inputElement.addEventListener("change", handleFiles, false); 4 | 5 | var canvas1 = document.getElementById('canvas1'); 6 | var ctx1 = canvas1.getContext('2d'); 7 | 8 | 9 | // handle input file 10 | function handleFiles(e) { 11 | console.log("Running handleFiles on " + e + ", e.target.result: " + e.target.files[0]); 12 | 13 | var file = e.target.files[0]; 14 | var reader = new FileReader(); 15 | reader.addEventListener("load", processImage, false); 16 | reader.readAsArrayBuffer(file); 17 | } 18 | 19 | // process an a bitmap image 20 | function processImage(e) { 21 | console.log("Running processImage on " + e + ", e.target.result: " + e.target.result); 22 | 23 | // convert BMP to image data 24 | var buffer = e.target.result; 25 | var bitmap = getBMP(buffer); 26 | var imageData = convertToImageData(bitmap); 27 | 28 | // display on a canvas 29 | ctx1.putImageData(imageData, 0, 0); 30 | 31 | // convert to a data array 32 | var dataArray = convertToDataArray(imageData); 33 | 34 | // convert data array into a hex string 35 | var output = generateHexArray(dataArray, 1); 36 | 37 | console.log(output); 38 | } 39 | 40 | // process a bitmap header and data 41 | function getBMP(buffer) { 42 | var datav = new DataView(buffer); 43 | var bitmap = {}; 44 | 45 | bitmap.fileheader = {}; 46 | bitmap.fileheader.bfType = datav.getUint16(0, true); // bitmap type 47 | bitmap.fileheader.bfSize = datav.getUint32(2, true); // bitmap size in bytes 48 | bitmap.fileheader.bfReserved1 = datav.getUint16(6, true); // reserved 49 | bitmap.fileheader.bfReserved2 = datav.getUint16(8, true); // reserved 50 | bitmap.fileheader.bfOffBits = datav.getUint32(10, true); // starting address of image data 51 | 52 | bitmap.infoheader = {}; 53 | bitmap.infoheader.biSize = datav.getUint32(14, true); // header size 54 | bitmap.infoheader.biWidth = datav.getUint32(18, true); // width in pixels 55 | bitmap.infoheader.biHeight = datav.getUint32(22, true); // height in pixels 56 | bitmap.infoheader.biPlanes = datav.getUint16(26, true); // number of color planes 57 | bitmap.infoheader.biBitCount = datav.getUint16(28, true); // number of bits per pixel 58 | bitmap.infoheader.biCompression = datav.getUint32(30, true); // compression method 59 | bitmap.infoheader.biSizeImage = datav.getUint32(34, true); // image size (raw image data) 60 | bitmap.infoheader.biXPelsPerMeter = datav.getUint32(38, true); // horizontal resolution (pixel per meter) 61 | bitmap.infoheader.biYPelsPerMeter = datav.getUint32(42, true); // vertical resolution (pixels per meter) 62 | bitmap.infoheader.biClrUsed = datav.getUint32(46, true); // number of colors 63 | bitmap.infoheader.biClrImportant = datav.getUint32(50, true); // number of important collors 64 | 65 | var start = bitmap.fileheader.bfOffBits; 66 | bitmap.stride = Math.floor( 67 | (bitmap.infoheader.biBitCount * 68 | bitmap.infoheader.biWidth+31)/32)*4; 69 | bitmap.pixels = new Uint8Array(buffer, start); 70 | 71 | console.log("Read BMP file data, image: " + bitmap.infoheader.biWidth + "x" + bitmap.infoheader.biHeight); 72 | 73 | return bitmap; 74 | } 75 | 76 | // read bitmap into a data-array 77 | // location data 78 | // data[i+0] R 79 | // data[i+1] G 80 | // data[i+3] B 81 | // data[i+4] A 82 | function convertToImageData(bitmap) { 83 | canvas = document.createElement("canvas"); 84 | 85 | var ctx = canvas.getContext("2d"); 86 | var Width = bitmap.infoheader.biWidth; 87 | var Height = bitmap.infoheader.biHeight; 88 | var imageData = ctx.createImageData(Width, Height); 89 | var data = imageData.data; 90 | var bmpdata = bitmap.pixels; 91 | var stride = bitmap.stride; 92 | 93 | for (var y = 0; y < Height; ++y) { 94 | for (var x = 0; x < Width; ++x) { 95 | var index1 = (x + Width*(Height-1-y) ) * 4; 96 | var index2 = x * 3 + stride * y; 97 | 98 | data[index1] = bmpdata[index2 + 2]; // Red component 99 | data[index1 + 1] = bmpdata[index2 + 1]; // Green component 100 | data[index1 + 2] = bmpdata[index2]; // Blue component 101 | data[index1 + 3] = 255; // Alpha component 102 | } 103 | } 104 | 105 | return imageData; 106 | } 107 | 108 | // convert an RGB array to a greyscale array 109 | function convertToGrayscale(colorImageData) { 110 | var colorData = colorImageData.data; 111 | 112 | console.log("Starting conversion loop, colorData.length = " + colorData.length); 113 | // convert color to greyscale 114 | for(var i = 0; i < colorData.length; i += 4) { 115 | var brightness = 0.34 * colorData[i] + 0.5 * colorData[i + 1] + 0.16 * colorData[i + 2]; 116 | // red 117 | colorData[i] = brightness; 118 | // green 119 | colorData[i + 1] = brightness; 120 | // blue 121 | colorData[i + 2] = brightness; 122 | } 123 | 124 | 125 | // return the grayscale data 126 | return colorImageData; 127 | } 128 | 129 | 130 | 131 | function convertToDataArray(imageData) { 132 | var data = imageData.data; 133 | 134 | console.log("convertToDataArray: ", imageData); 135 | 136 | var cols = imageData.width; 137 | var rows = imageData.height; 138 | var lcdArr = []; 139 | var index = 0; 140 | 141 | // generate the array 142 | for (var i = 0; i < rows; i += 8) // page index 143 | { 144 | for (var j = 0; j < cols; j++) // column index 145 | { 146 | // find the current lcd index 147 | index = (i * cols)/8 + j; 148 | 149 | // initialize the lcd datapoint 150 | lcdArr[index] = 0; 151 | 152 | // loop through the rows in this current page+column 153 | for (var k = 0; k < 8; k++) // row index 154 | { 155 | //console.log("page: " + i + ", column: " + j + ", bit: " + k + ", lcd index = " + index); 156 | 157 | // find if this pixel should be represented by black or white 158 | var val = colorToBlackOrWhite(imageData, j, i+k); 159 | 160 | // push the b+w value into the byte 161 | // lcd byte ordering: 162 | // LSB is at the top of the page 163 | lcdArr[index] = lcdArr[index] | ((val & 0x01) << k); 164 | } 165 | } 166 | } 167 | 168 | return lcdArr; 169 | } 170 | 171 | // convert an RGB color value to black or white 172 | function colorToBlackOrWhite(imageData, x,y) 173 | { 174 | var index = (y * imageData.width + x) * 4; 175 | var data = imageData.data; 176 | var colors = [data[index], data[index+1], data[index+2] ]; 177 | 178 | var average=0; 179 | for (var h=0;h<3;h++) 180 | { 181 | if (colors[h]==undefined) { 182 | colors[h]=255; 183 | } 184 | average=average+colors[h]; 185 | } 186 | average=average/3; 187 | average=Math.round(average); 188 | 189 | if (average>128) { 190 | // average color is bright, should be white 191 | return 0; 192 | } 193 | else { 194 | // average color is dim, should be black 195 | return 1; 196 | } 197 | } 198 | 199 | // take an array and output it as a hex string 200 | // outputOption 201 | // 0 - output file will be in following format: 0x00,0x01,0x02,0xfa,0x40, 202 | // 1 - output file will be in following format: 000102fa40 203 | function generateHexArray(dataArr, outputOption) { 204 | var hexArray = ""; 205 | 206 | for (var i = 0; i < dataArr.length; i++) { 207 | if (outputOption === 0) { 208 | hexArray += "0x"; 209 | hexArray += dec2hex(dataArr[i]); 210 | hexArray += ","; 211 | } 212 | else if (outputOption === 1) { 213 | hexArray += dec2hex(dataArr[i]); 214 | } 215 | } 216 | 217 | return hexArray; 218 | } 219 | 220 | // convert a decimal into hex 221 | // including padding for values less than 16 222 | function dec2hex(i) 223 | { 224 | var result = "00"; 225 | if (i >= 0 && i <= 15) { result = "0" + i.toString(16); } 226 | else if (i >= 16 && i <= 255) { result = i.toString(16); } 227 | return result; 228 | } 229 | -------------------------------------------------------------------------------- /bmp2lcd/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | Convert BMP to Data Array 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Convert BMP to Data Array

22 |
23 | 24 | 25 |
26 | 27 | This text is displayed if your browser does not support HTML5 Canvas. 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/oled-exp.py: -------------------------------------------------------------------------------- 1 | from OmegaExpansion import oledExp 2 | import time 3 | 4 | 5 | print('Starting to use oled-exp functions...') 6 | 7 | oledExp.setVerbosity(0) 8 | 9 | 10 | # initialize 11 | ret = oledExp.driverInit() 12 | print("driverInit return: %s"%ret) 13 | if (ret != 0): 14 | exit() 15 | 16 | # write a character 17 | ret = oledExp.writeChar('x') 18 | print("writeChar return: %s"%ret) 19 | if (ret != 0): 20 | exit() 21 | time.sleep(2) 22 | 23 | # invert the colours 24 | ret = oledExp.setDisplayMode(1) 25 | print("setDisplayMode return: %s"%ret) 26 | if (ret != 0): 27 | exit() 28 | time.sleep(2) 29 | 30 | # dim 31 | ret = oledExp.setDim(1) 32 | print("setDim return: %s"%ret) 33 | if (ret != 0): 34 | exit() 35 | time.sleep(2) 36 | 37 | # clear the display 38 | ret = oledExp.clear() 39 | print("clear return: %s"%ret) 40 | if (ret != 0): 41 | exit() 42 | time.sleep(2) 43 | 44 | # write a message 45 | ret = oledExp.write("Welcome to the Omega") 46 | print("write return: %s"%ret) 47 | if (ret != 0): 48 | exit() 49 | time.sleep(2) 50 | 51 | # set the cursor 52 | ret = oledExp.setCursor(3,8) 53 | print("write return: %s"%ret) 54 | if (ret != 0): 55 | exit() 56 | 57 | # write a message 58 | ret = oledExp.write("Invent the future") 59 | print("write return: %s"%ret) 60 | if (ret != 0): 61 | exit() 62 | time.sleep(2) 63 | 64 | # invert the colours to normal 65 | ret = oledExp.setDisplayMode(0) 66 | print("setDisplayMode return: %s"%ret) 67 | if (ret != 0): 68 | exit() 69 | time.sleep(2) 70 | 71 | # set the highest brightness 72 | ret = oledExp.setBrightness(255) 73 | print("setBrightness return: %s"%ret) 74 | if (ret != 0): 75 | exit() 76 | time.sleep(2) 77 | 78 | 79 | # clear the display 80 | ret = oledExp.clear() 81 | print("clear return: %s"%ret) 82 | if (ret != 0): 83 | exit() 84 | 85 | # set the cursor by pixel 86 | ret = oledExp.setCursorByPixel(1,64) 87 | print("setCursorByPixel return: %s"%ret) 88 | if (ret != 0): 89 | exit() 90 | 91 | # draw a few bytes 92 | ret = oledExp.writeByte(0x0f) 93 | print("writeByte return: %s"%ret) 94 | ret = oledExp.writeByte(0xf0) 95 | print("writeByte return: %s"%ret) 96 | ret = oledExp.writeByte(0x0f) 97 | print("writeByte return: %s"%ret) 98 | ret = oledExp.writeByte(0xf0) 99 | print("writeByte return: %s"%ret) 100 | time.sleep(2) 101 | 102 | ret = oledExp.setCursorByPixel(1,127) 103 | print("setCursorByPixel return: %s"%ret) 104 | if (ret != 0): 105 | exit() 106 | 107 | # draw a few bytes 108 | ret = oledExp.writeByte(0x0f) 109 | print("writeByte return: %s"%ret) 110 | ret = oledExp.writeByte(0xf0) 111 | print("writeByte return: %s"%ret) 112 | ret = oledExp.writeByte(0x0f) 113 | print("writeByte return: %s"%ret) 114 | ret = oledExp.writeByte(0xf0) 115 | print("writeByte return: %s"%ret) 116 | 117 | 118 | # clear the display 119 | #ret = oledExp.clear() 120 | #print("clear return: %s"%ret) 121 | #if (ret != 0): 122 | # exit() 123 | #time.sleep(2) 124 | 125 | # draw an image 126 | #ret = oledExp.drawFromFile("lcd/onion.lcd") 127 | #print("drawFromFile return: %s"%ret) 128 | #if (ret != 0): 129 | # exit() 130 | #time.sleep(2) 131 | 132 | 133 | 134 | print("Done") 135 | -------------------------------------------------------------------------------- /examples/onion-i2c.py: -------------------------------------------------------------------------------- 1 | from OmegaExpansion import onionI2C 2 | import time 3 | import sys 4 | 5 | print('Starting: onionI2C module testing...') 6 | 7 | i2c = onionI2C.OnionI2C(0) 8 | 9 | # set the verbosity 10 | i2c.setVerbosity(1) 11 | 12 | devAddr = 0x27 13 | 14 | 15 | print("") 16 | if sys.version_info[0] < 3: 17 | ret = raw_input(' Ready to read?') 18 | else: 19 | ret = input(' Ready to read?') 20 | 21 | # perform series of reads 22 | size = 1 23 | addr = 0x00 24 | print('Reading from device 0x%02x, address: 0x%02x'%(devAddr, addr)) 25 | val = i2c.readBytes(devAddr, addr, size) 26 | print(' Read returned: %s'%(val)) 27 | 28 | size = 1 29 | addr = 0x01 30 | print('Reading from device 0x%02x, address: 0x%02x'%(devAddr, addr)) 31 | val = i2c.readBytes(devAddr, addr, size) 32 | print(' Read returned: %s'%(val)) 33 | 34 | size = 2 35 | addr = 0x06 36 | print('Reading from device 0x%02x, address: 0x%02x'%(devAddr, addr)) 37 | val = i2c.readBytes(devAddr, addr, size) 38 | print(' Read returned: %s'%(val)) 39 | 40 | 41 | 42 | print("") 43 | if sys.version_info[0] < 3: 44 | ret = raw_input(' Ready to write?') 45 | else: 46 | ret = input(' Ready to write?') 47 | 48 | # perform series of writes 49 | size = 1 50 | addr = 0x00 51 | value = [0x00] 52 | print('Writing to device 0x%02x, address: 0x%02x'%(devAddr, addr)) 53 | val = i2c.writeBytes(devAddr, addr, value) 54 | print(' writeBytes returned: %s'%(val)) 55 | 56 | size = 1 57 | addr = 0x01 58 | value = 0x00 59 | print('Writing to device 0x%02x, address: 0x%02x'%(devAddr, addr)) 60 | # comment: confirmed that this causes a TypeError :) 61 | # val = i2c.writeBytes(devAddr, addr, value) 62 | # print(' writeBytes returned: ', val) 63 | val = i2c.writeByte(devAddr, addr, value) 64 | print(' writeByte returned: %s'%(val)) 65 | 66 | size = 2 67 | value = [0x06, 0x00] 68 | print('Writing to device 0x%02x, list is: %s'%(devAddr, value)) 69 | val = i2c.write(devAddr, value) 70 | print(' write returned: %s'%(val)) 71 | 72 | 73 | 74 | print("") 75 | if sys.version_info[0] < 3: 76 | ret = raw_input(' Ready to test writing? Part 1') 77 | else: 78 | ret = input(' Ready to test writing? Part 1') 79 | 80 | # perform write 81 | size = 1 82 | addr = 0x09 83 | value = [0x01] 84 | print('Writing to device 0x%02x, address: 0x%02x, writing: 0x%02x'%(devAddr, addr, value[0])) 85 | val = i2c.writeBytes(devAddr, addr, value) 86 | print(' writeBytes returned: %s'%(val)) 87 | 88 | # read back the value 89 | size = 1 90 | addr = 0x09 91 | print('Reading from device 0x%02x, address: 0x%02x'%(devAddr, addr)) 92 | val = i2c.readBytes(devAddr, addr, size) 93 | print(' Read returned: %s'%(val)) 94 | 95 | 96 | 97 | 98 | print("") 99 | if sys.version_info[0] < 3: 100 | ret = raw_input(' Ready to test writing? Part 2') 101 | else: 102 | ret = input(' Ready to test writing? Part 2') 103 | 104 | # perform write 105 | size = 1 106 | addr = 0x09 107 | value = 0x02 108 | print('Writing to device 0x%02x, address: 0x%02x, writing: 0x%02x'%(devAddr, addr, value)) 109 | val = i2c.writeByte(devAddr, addr, value) 110 | print(' writeByte returned: %s'%(val)) 111 | 112 | # read back the value 113 | size = 1 114 | addr = 0x09 115 | print('Reading from device 0x%02x, address: 0x%02x'%(devAddr, addr)) 116 | val = i2c.readBytes(devAddr, addr, size) 117 | print(' Read returned: %s'%(val)) 118 | 119 | 120 | 121 | 122 | print("") 123 | if sys.version_info[0] < 3: 124 | ret = raw_input(' Ready to test writing? Part 3') 125 | else: 126 | ret = input(' Ready to test writing? Part 3') 127 | 128 | # perform write 129 | size = 2 130 | value = [0x09, 0x03] 131 | print('Writing to device 0x%02x, list is: %s'%(devAddr, value)) 132 | val = i2c.write(devAddr, value) 133 | print(' write returned: %s'%(val)) 134 | 135 | # read back the value 136 | size = 1 137 | addr = 0x09 138 | print('Reading from device 0x%02x, address: 0x%02x'%(devAddr, addr)) 139 | val = i2c.readBytes(devAddr, addr, size) 140 | print(' Read returned: %s'%(val)) 141 | 142 | 143 | 144 | print("") 145 | if sys.version_info[0] < 3: 146 | ret= raw_input(' Ready finish testing?') 147 | else: 148 | ret= input(' Ready finish testing?') 149 | 150 | # perform write 151 | size = 1 152 | addr = 0x09 153 | value = 0x00 154 | print('Writing to device 0x%02x, address: 0x%02x, writing: 0x%02x'%(devAddr, addr, value)) 155 | val = i2c.writeByte(devAddr, addr, value) 156 | print(' writeByte returned: %s'%(val)) 157 | 158 | 159 | 160 | 161 | print('Done!') 162 | -------------------------------------------------------------------------------- /examples/pwm-exp.py: -------------------------------------------------------------------------------- 1 | from OmegaExpansion import pwmExp 2 | import time 3 | import sys 4 | 5 | # check the arguments 6 | channel = 0 7 | if len(sys.argv) == 2: 8 | channel = int(sys.argv[1]) 9 | 10 | print('>> Using channel %d'%channel) 11 | 12 | pwmExp.setVerbosity(0) 13 | 14 | # check initialization 15 | # should return 0 if the PWM Expansion has just been plugged in 16 | ret = pwmExp.checkInit() 17 | print("checking if initialized: %s"%ret) 18 | 19 | # initialize the pwm-exp 20 | ret = pwmExp.driverInit() 21 | print("driverInit return: %s"%ret) 22 | if (ret != 0): 23 | exit() 24 | ret = pwmExp.setFrequency(50) 25 | print("setFrequency return: %s"%ret) 26 | if (ret != 0): 27 | exit() 28 | 29 | # check initialization 30 | # should return 1 since the Expansion was initialized above 31 | ret = pwmExp.checkInit() 32 | print("checking if initialized: %s"%ret) 33 | time.sleep(1) 34 | 35 | 36 | # set channel 0 37 | ret = pwmExp.setupDriver(channel, 9, 0) 38 | print("setupDriver return: %s"%ret) 39 | if (ret != 0): 40 | exit() 41 | time.sleep(2) 42 | 43 | # set channel 0 44 | ret = pwmExp.setupDriver(channel, 5, 0) 45 | print("setupDriver return: %s"%ret) 46 | if (ret != 0): 47 | exit() 48 | time.sleep(2) 49 | 50 | # change the frequency 51 | ret = pwmExp.setFrequency(60) 52 | print("setFrequency return: %s"%ret) 53 | if (ret != 0): 54 | exit() 55 | time.sleep(2) 56 | 57 | # set channel 0 58 | ret = pwmExp.setupDriver(channel, 6.55, 0) 59 | print("setupDriver return: %s"%ret) 60 | if (ret != 0): 61 | exit() 62 | time.sleep(2) 63 | 64 | 65 | print("Done") 66 | -------------------------------------------------------------------------------- /examples/relay-exp.py: -------------------------------------------------------------------------------- 1 | from OmegaExpansion import relayExp 2 | import time 3 | import sys 4 | 5 | # check the arguments 6 | if len(sys.argv) != 2: 7 | print('ERROR: expected addr offset:') 8 | print('%s '%sys.argv[0]) 9 | exit() 10 | 11 | addr = int(sys.argv[1]) 12 | 13 | print('Starting to use relay-exp functions on addr offset %d'%addr) 14 | 15 | relayExp.setVerbosity(0) 16 | 17 | # check initialization 18 | # should return 0 if the Expansion has just been plugged in 19 | ret = relayExp.checkInit(addr) 20 | print("checking if initialized: %s"%ret) 21 | 22 | # initialize the relay-exp 23 | ret = relayExp.driverInit(addr) 24 | print("Result from relayDriverInit: %s"%ret) 25 | if (ret != 0): 26 | exit() 27 | 28 | # check initialization 29 | # should return 1 since the Expansion was initialized above 30 | ret = relayExp.checkInit(addr) 31 | print("checking if initialized: %s"%ret) 32 | time.sleep(1) 33 | 34 | 35 | # set channel 0 to on 36 | ret = relayExp.setChannel(addr, 0, 1) 37 | print("Result from relaySetChannel: %s"%ret) 38 | if (ret != 0): 39 | exit() 40 | time.sleep(2) 41 | 42 | # read channel 0 value 43 | value = relayExp.readChannel(addr, 0) 44 | print("Channel 0 value: %d"%value) 45 | time.sleep(2) 46 | 47 | # set both channels to on 48 | ret = relayExp.setAllChannels(addr, 1) 49 | print("Result from relaySetAllChannels: %s"%ret) 50 | if (ret != 0): 51 | exit() 52 | time.sleep(2) 53 | 54 | # set channel 0 to off 55 | ret = relayExp.setChannel(addr, 0, 0) 56 | print("Result from relaySetChannel: %s"%ret) 57 | if (ret != 0): 58 | exit() 59 | time.sleep(2) 60 | 61 | # read channel 0 value 62 | value = relayExp.readChannel(addr, 0) 63 | print("Channel 0 value: %d"%value) 64 | time.sleep(2) 65 | 66 | 67 | print("Done") 68 | -------------------------------------------------------------------------------- /examples/sierpenski-1306.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | #Sierpenski Triangle Example for ssd1306 OLED board (and Omega2 of course!) 3 | #by Casten Riepling 4 | #Thanks to greenbreakfast for the nice i2c-exp-driver library + examples! 5 | 6 | from OmegaExpansion import oledExp as oled 7 | import random 8 | import time 9 | 10 | #since we can only write a byte at a time, we need to keep 11 | #the previous state in memory to OR it with 12 | state = [[0 for x in range(16)] for y in range(128)] 13 | 14 | 15 | #init all the 1306 driver stuff 16 | oled.driverInit() 17 | oled.setImageColumns() 18 | oled.setDisplayMode(0) 19 | 20 | #init our positional state and boundary vertices 21 | currX = currY = 0 22 | vertices = [{'x':0,'y':0}, {'x':63,'y':63}, {'x':0,'y':127}] 23 | 24 | #To make it a little more fun, we'll use a diminishing wait so one can 25 | #get an idea of the algorithm at work 26 | sleepTime = 1.0 27 | 28 | #The algorithm works as follows. Start with 3 vertices of a triangle and begin at 29 | #one of them. Randomly choose a vertex. Go half way to that vertex and draw a point. 30 | #Repeat until you are happy. 31 | 32 | while True: 33 | #pick a vertex 34 | i = random.randint(0,2) 35 | vert = vertices[i] 36 | 37 | #find the point halfway between 38 | currX = int((vert['x']+currX) / 2) 39 | currY = int((vert['y']+currY) / 2) 40 | 41 | #convert to columns and bit patterns 42 | col = int(currX/8) 43 | bitX = 1<<(currX%8) 44 | 45 | #OR the info into the existing data 46 | state[currY][col] |= int(state[currY][col] | bitX) 47 | 48 | #write the data to the display 49 | oled.setCursorByPixel(col,currY) 50 | oled.writeByte(state[currY][col]) 51 | 52 | #and do some sleeping for dramatic effect 53 | time.sleep(sleepTime) 54 | sleepTime = sleepTime - (sleepTime/20) 55 | -------------------------------------------------------------------------------- /include/oled-exp.h: -------------------------------------------------------------------------------- 1 | #ifndef _OLED_EXP_H_ 2 | #define _OLED_EXP_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | // Constants 13 | #define OLED_EXP_ADDR 0x3C 14 | #define OLED_EXP_DEVICE_NUM (I2C_DEFAULT_ADAPTER) 15 | #define OLED_EXP_WIDTH 128 16 | #define OLED_EXP_HEIGHT 64 17 | #define OLED_EXP_PAGES 8 18 | #define OLED_EXP_CHAR_LENGTH 6 19 | #define OLED_EXP_NUM_CHARS 96 20 | 21 | #define OLED_EXP_CHAR_COLUMNS 21 22 | #define OLED_EXP_CHAR_ROWS 8 23 | #define OLED_EXP_CHAR_COLUMN_PIXELS (OLED_EXP_CHAR_COLUMNS * OLED_EXP_CHAR_LENGTH) 24 | 25 | #define OLED_EXP_CONTRAST_MIN 0 26 | #define OLED_EXP_CONTRAST_MAX 255 27 | 28 | #define OLED_EXP_DEF_CONTRAST_EXTERNAL_VCC 0x9f 29 | #define OLED_EXP_DEF_CONTRAST_SWITCH_CAP_VCC 0xcf 30 | 31 | // Registers 32 | #define OLED_EXP_REG_DATA 0x40 33 | #define OLED_EXP_REG_COMMAND 0x80 34 | 35 | 36 | // Addresses 37 | #define OLED_EXP_ADDR_BASE_PAGE_START 0xB0 38 | 39 | // Command Constants 40 | #define OLED_EXP_SET_CONTRAST 0x81 41 | #define OLED_EXP_DISPLAY_ALL_ON_RESUME 0xA4 42 | #define OLED_EXP_DISPLAY_ALL_ON 0xA5 43 | #define OLED_EXP_NORMAL_DISPLAY 0xA6 44 | #define OLED_EXP_INVERT_DISPLAY 0xA7 45 | #define OLED_EXP_DISPLAY_OFF 0xAE 46 | #define OLED_EXP_DISPLAY_ON 0xAF 47 | #define OLED_EXP_SET_DISPLAY_OFFSET 0xD3 48 | #define OLED_EXP_SET_COM_PINS 0xDA 49 | #define OLED_EXP_SET_VCOM_DETECT 0xDB 50 | #define OLED_EXP_SET_DISPLAY_CLOCK_DIV 0xD5 51 | #define OLED_EXP_SET_PRECHARGE 0xD9 52 | #define OLED_EXP_SET_MULTIPLEX 0xA8 53 | #define OLED_EXP_SET_LOW_COLUMN 0x00 54 | #define OLED_EXP_SET_HIGH_COLUMN 0x10 55 | #define OLED_EXP_SET_START_LINE 0x40 56 | #define OLED_EXP_MEMORY_MODE 0x20 57 | 58 | typedef enum e_OledExpMemoryMode { 59 | OLED_EXP_MEM_HORIZONTAL_ADDR_MODE = 0x00, 60 | OLED_EXP_MEM_VERTICAL_ADDR_MODE = 0x01, 61 | OLED_EXP_MEM_PAGE_ADDR_MODE = 0x02, 62 | OLED_EXP_MEM_NUM_MODES = 3 63 | } eOledExpMemoryMode; 64 | 65 | #define OLED_EXP_COLUMN_ADDR 0x21 66 | #define OLED_EXP_PAGE_ADDR 0x22 67 | #define OLED_EXP_COM_SCAN_INC 0xC0 68 | #define OLED_EXP_COM_SCAN_DEC 0xC8 69 | #define OLED_EXP_SEG_REMAP 0xA0 70 | #define OLED_EXP_CHARGE_PUMP 0x8D 71 | #define OLED_EXP_EXTERNAL_VCC 0x01 72 | #define OLED_EXP_SWITCH_CAP_VCC 0x02 73 | 74 | // Scrolling Constants 75 | #define OLED_EXP_ACTIVATE_SCROLL 0x2F 76 | #define OLED_EXP_DEACTIVATE_SCROLL 0x2E 77 | #define OLED_EXP_SET_VERTICAL_SCROLL_AREA 0xA3 78 | #define OLED_EXP_RIGHT_HORIZONTAL_SCROLL 0x26 79 | #define OLED_EXP_LEFT_HORIZONTAL_SCROLL 0x27 80 | #define OLED_EXP_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 81 | #define OLED_EXP_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A 82 | 83 | typedef enum e_OledExpScrollSpeed { 84 | OLED_EXP_SCROLL_SPEED_5_FRAMES = 0x00, 85 | OLED_EXP_SCROLL_SPEED_64_FRAMES = 0x01, 86 | OLED_EXP_SCROLL_SPEED_128_FRAMES = 0x02, 87 | OLED_EXP_SCROLL_SPEED_256_FRAMES = 0x03, 88 | OLED_EXP_SCROLL_SPEED_3_FRAMES = 0x04, 89 | OLED_EXP_SCROLL_SPEED_4_FRAMES = 0x05, 90 | OLED_EXP_SCROLL_SPEED_25_FRAMES = 0x06, 91 | OLED_EXP_SCROLL_SPEED_2_FRAMES = 0x07, 92 | OLED_EXP_SCROLL_SPEED_NUM 93 | } eOledExpScrollSpeed; 94 | 95 | 96 | #define OLED_EXP_READ_LCD_STRING_OPT0 "0x%02x," 97 | #define OLED_EXP_READ_LCD_STRING_OPT1 "%2x" 98 | #define OLED_EXP_READ_LCD_DATA_IDENTIFIER "data:" 99 | 100 | #define MAX_PARAM_LENGTH (strlen(OLED_EXP_READ_LCD_DATA_IDENTIFIER) + (OLED_EXP_WIDTH*OLED_EXP_HEIGHT/8)*2) 101 | #define MAX_COMMAND_LENGTH 255 102 | 103 | // Ascii Table 104 | static const uint8_t asciiTable[][OLED_EXP_CHAR_LENGTH] = { 105 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // SPACE 106 | 107 | {0x00, 0x00, 0x4F, 0x00, 0x00, 0x00}, // ! 108 | {0x00, 0x07, 0x00, 0x07, 0x00, 0x00}, // " 109 | {0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00}, // # 110 | {0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00}, // $ 111 | {0x23, 0x13, 0x08, 0x64, 0x62, 0x00}, // % 112 | {0x36, 0x49, 0x55, 0x22, 0x50, 0x00}, // & 113 | {0x00, 0x05, 0x03, 0x00, 0x00, 0x00}, // ' 114 | {0x00, 0x1C, 0x22, 0x41, 0x00, 0x00}, // ( 115 | {0x00, 0x41, 0x22, 0x1C, 0x00, 0x00}, // ) 116 | {0x14, 0x08, 0x3E, 0x08, 0x14, 0x00}, // * 117 | {0x08, 0x08, 0x3E, 0x08, 0x08, 0x00}, // + 118 | {0x00, 0x50, 0x30, 0x00, 0x00, 0x00}, // , 119 | {0x08, 0x08, 0x08, 0x08, 0x08, 0x00}, // - 120 | {0x00, 0x60, 0x60, 0x00, 0x00, 0x00}, // . 121 | {0x20, 0x10, 0x08, 0x04, 0x02, 0x00}, // / 122 | 123 | {0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00}, // 0 124 | {0x00, 0x42, 0x7F, 0x40, 0x00, 0x00}, // 1 125 | {0x42, 0x61, 0x51, 0x49, 0x46, 0x00}, // 2 126 | {0x21, 0x41, 0x45, 0x4B, 0x31, 0x00}, // 3 127 | {0x18, 0x14, 0x12, 0x7F, 0x10, 0x00}, // 4 128 | {0x27, 0x45, 0x45, 0x45, 0x39, 0x00}, // 5 129 | {0x3C, 0x4A, 0x49, 0x49, 0x30, 0x00}, // 6 130 | {0x01, 0x71, 0x09, 0x05, 0x03, 0x00}, // 7 131 | {0x36, 0x49, 0x49, 0x49, 0x36, 0x00}, // 8 132 | {0x06, 0x49, 0x49, 0x29, 0x1E, 0x00}, // 9 133 | 134 | {0x36, 0x36, 0x00, 0x00, 0x00, 0x00}, // : 135 | {0x56, 0x36, 0x00, 0x00, 0x00, 0x00}, // ; 136 | {0x08, 0x14, 0x22, 0x41, 0x00, 0x00}, // < 137 | {0x14, 0x14, 0x14, 0x14, 0x14, 0x00}, // = 138 | {0x00, 0x41, 0x22, 0x14, 0x08, 0x00}, // > 139 | {0x02, 0x01, 0x51, 0x09, 0x06, 0x00}, // ? 140 | {0x30, 0x49, 0x79, 0x41, 0x3E, 0x00}, // @ 141 | 142 | {0x7E, 0x11, 0x11, 0x11, 0x7E, 0x00}, // A 143 | {0x7F, 0x49, 0x49, 0x49, 0x36, 0x00}, // B 144 | {0x3E, 0x41, 0x41, 0x41, 0x22, 0x00}, // C 145 | {0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00}, // D 146 | {0x7F, 0x49, 0x49, 0x49, 0x41, 0x00}, // E 147 | {0x7F, 0x09, 0x09, 0x09, 0x01, 0x00}, // F 148 | {0x3E, 0x41, 0x49, 0x49, 0x7A, 0x00}, // G 149 | {0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00}, // H 150 | {0x00, 0x41, 0x7F, 0x41, 0x00, 0x00}, // I 151 | {0x20, 0x40, 0x41, 0x3F, 0x01, 0x00}, // J 152 | {0x7F, 0x08, 0x14, 0x22, 0x41, 0x00}, // K 153 | {0x7F, 0x40, 0x40, 0x40, 0x40, 0x00}, // L 154 | {0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x00}, // M 155 | {0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00}, // N 156 | {0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00}, // O 157 | {0x7F, 0x09, 0x09, 0x09, 0x06, 0x00}, // P 158 | {0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00}, // Q 159 | {0x7F, 0x09, 0x19, 0x29, 0x46, 0x00}, // R 160 | {0x46, 0x49, 0x49, 0x49, 0x31, 0x00}, // S 161 | {0x01, 0x01, 0x7F, 0x01, 0x01, 0x00}, // T 162 | {0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00}, // U 163 | {0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00}, // V 164 | {0x3F, 0x40, 0x30, 0x40, 0x3F, 0x00}, // W 165 | {0x63, 0x14, 0x08, 0x14, 0x63, 0x00}, // X 166 | {0x07, 0x08, 0x70, 0x08, 0x07, 0x00}, // Y 167 | {0x61, 0x51, 0x49, 0x45, 0x43, 0x00}, // Z 168 | 169 | {0x00, 0x7F, 0x41, 0x41, 0x00, 0x00}, // [ 170 | {0x02, 0x04, 0x08, 0x10, 0x20, 0x00}, // backslash 171 | {0x00, 0x41, 0x41, 0x7F, 0x00, 0x00}, // ] 172 | {0x04, 0x02, 0x01, 0x02, 0x04, 0x00}, // ^ 173 | {0x40, 0x40, 0x40, 0x40, 0x40, 0x00}, // _ 174 | {0x00, 0x01, 0x02, 0x04, 0x00, 0x00}, // ` 175 | 176 | {0x20, 0x54, 0x54, 0x54, 0x78, 0x00}, // a 177 | {0x7F, 0x50, 0x48, 0x48, 0x30, 0x00}, // b 178 | {0x38, 0x44, 0x44, 0x44, 0x20, 0x00}, // c 179 | {0x38, 0x44, 0x44, 0x48, 0x7F, 0x00}, // d 180 | {0x38, 0x54, 0x54, 0x54, 0x18, 0x00}, // e 181 | {0x08, 0x7E, 0x09, 0x01, 0x02, 0x00}, // f 182 | {0x0C, 0x52, 0x52, 0x52, 0x3E, 0x00}, // g 183 | {0x7F, 0x08, 0x04, 0x04, 0x78, 0x00}, // h 184 | {0x00, 0x44, 0x7D, 0x40, 0x00, 0x00}, // i 185 | {0x20, 0x40, 0x44, 0x3D, 0x00, 0x00}, // j 186 | {0x7F, 0x10, 0x28, 0x44, 0x00, 0x00}, // k 187 | {0x00, 0x41, 0x7F, 0x40, 0x00, 0x00}, // l 188 | {0x78, 0x04, 0x78, 0x04, 0x78, 0x00}, // m 189 | {0x7C, 0x08, 0x04, 0x04, 0x78, 0x00}, // n 190 | {0x38, 0x44, 0x44, 0x44, 0x38, 0x00}, // o 191 | {0x7C, 0x14, 0x14, 0x14, 0x08, 0x00}, // p 192 | {0x08, 0x14, 0x14, 0x18, 0x7C, 0x00}, // q 193 | {0x7C, 0x08, 0x04, 0x04, 0x08, 0x00}, // r 194 | {0x48, 0x54, 0x54, 0x54, 0x20, 0x00}, // s 195 | {0x04, 0x3F, 0x44, 0x40, 0x20, 0x00}, // t 196 | {0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00}, // u 197 | {0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00}, // v 198 | {0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00}, // w 199 | {0x44, 0x28, 0x10, 0x28, 0x44, 0x00}, // x 200 | {0x0C, 0x50, 0x50, 0x50, 0x3C, 0x00}, // y 201 | {0x44, 0x64, 0x54, 0x4C, 0x44, 0x00}, // z 202 | 203 | {0x00, 0x08, 0x36, 0x41, 0x00, 0x00}, // { 204 | {0x00, 0x00, 0x7F, 0x00, 0x00, 0x00}, // | 205 | {0x00, 0x41, 0x36, 0x08, 0x00, 0x00}, // } 206 | {0x0C, 0x02, 0x0C, 0x10, 0x0C, 0x00}, // ~ 207 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 208 | }; 209 | 210 | // Variables 211 | int _vccState; 212 | int _memoryMode; 213 | 214 | int _buffer[OLED_EXP_WIDTH * OLED_EXP_PAGES]; 215 | int _cursor; 216 | 217 | int _cursorInRow; 218 | int _bColumnsSetForText; 219 | 220 | #ifdef __cplusplus 221 | extern "C" { 222 | #endif 223 | 224 | //// Functions 225 | int _oledSendCommand (int command); 226 | int _oledSendData (int data); 227 | 228 | // initialization and clearing 229 | int oledDriverInit (); 230 | int oledCheckInit (); 231 | int oledClear (); 232 | 233 | // configuration 234 | int oledSetDisplayPower (int bPowerOn); 235 | int oledSetDisplayMode (int bInvert); 236 | int oledSetBrightness (int brightness); 237 | int oledSetDim (int dim); 238 | int oledSetMemoryMode (int mode); 239 | 240 | int oledSetCursor (int row, int column); 241 | int oledSetCursorByPixel (int row, int pixel); 242 | 243 | int oledSetColumnAddressing (int startPixel, int endPixel); 244 | int oledSetTextColumns (); 245 | int oledSetImageColumns (); 246 | 247 | // writing to the display 248 | int oledWriteChar (char c); 249 | int oledWrite (char *msg); 250 | int oledWriteByte (int byte); 251 | 252 | int oledDraw (uint8_t *buffer, int bytes); 253 | 254 | // scroll the display 255 | int oledScroll (int direction, int scrollSpeed, int startPage, int stopPage); 256 | int oledScrollDiagonal (int direction, int scrollSpeed, int fixedRows, int scrollRows, int verticalOffset, int startPage, int stopPage); 257 | int oledScrollStop (); 258 | 259 | // reading lcd data 260 | int oledReadLcdFile (char* file, uint8_t *buffer); 261 | int oledReadLcdData (char* data, uint8_t *buffer); 262 | 263 | // writing to the buffer 264 | int oledDisplay (); 265 | int oledPrintChar (char c); 266 | 267 | int oledLineScroll (); 268 | int oledNewLine (); 269 | int oledPrintLine (); 270 | 271 | #ifdef __cplusplus 272 | } 273 | #endif 274 | #endif // _OLED_EXP_H_ 275 | -------------------------------------------------------------------------------- /include/onion-debug.h: -------------------------------------------------------------------------------- 1 | #ifndef _ONION_DEBUG_H_ 2 | #define _ONION_DEBUG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define ONION_SEVERITY_FATAL (-1) 9 | #define ONION_SEVERITY_INFO (0) 10 | #define ONION_SEVERITY_DEBUG (1) 11 | #define ONION_SEVERITY_DEBUG_EXTRA (2) 12 | 13 | #define ONION_VERBOSITY_NONE (ONION_SEVERITY_FATAL) 14 | #define ONION_VERBOSITY_NORMAL (ONION_SEVERITY_INFO) 15 | #define ONION_VERBOSITY_VERBOSE (ONION_SEVERITY_DEBUG) 16 | #define ONION_VERBOSITY_EXTRA_VERBOSE (ONION_SEVERITY_DEBUG_EXTRA) 17 | 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | #ifdef __cplusplus 24 | extern 25 | #endif 26 | int onionVerbosityLevel; 27 | 28 | // debug functions 29 | void onionSetVerbosity (int verbosityLevel); 30 | int onionGetVerbosity (); 31 | 32 | void onionPrint (int severity, const char* msg, ...); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif // _ONION_DEBUG_H_ 39 | -------------------------------------------------------------------------------- /include/onion-i2c.h: -------------------------------------------------------------------------------- 1 | #ifndef _ONION_I2C_H_ 2 | #define _ONION_I2C_H_ 3 | 4 | #include 5 | #include 6 | 7 | #ifndef __APPLE__ 8 | #include 9 | #include 10 | #endif 11 | 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | 24 | #define I2C_DEV_PATH "/dev/i2c-%d" 25 | #define I2C_PRINT_BANNER "onion-i2c::" 26 | 27 | #define I2C_BUFFER_SIZE 256 28 | 29 | #define I2C_DEFAULT_ADAPTER 0 30 | 31 | 32 | // for debugging 33 | #ifndef __APPLE__ 34 | #define I2C_ENABLED 1 35 | #endif 36 | 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | 43 | 44 | // helper functions 45 | int _i2c_getFd (int adapterNum, int *devHandle); 46 | int _i2c_releaseFd (int devHandle); 47 | 48 | int _i2c_setDevice (int devHandle, int addr); 49 | int _i2c_setDevice10bit (int devHandle, int addr); 50 | 51 | int _i2c_writeBuffer (int devNum, int devAddr, uint8_t *buffer, int size); 52 | 53 | 54 | // i2c functions 55 | int i2c_writeBuffer (int devNum, int devAddr, int addr, uint8_t *buffer, int size); 56 | int i2c_writeBufferRaw (int devNum, int devAddr, uint8_t *buffer, int size); 57 | int i2c_write (int devNum, int devAddr, int addr, int val); 58 | int i2c_writeBytes (int devNum, int devAddr, int addr, int val, int numBytes); 59 | 60 | 61 | 62 | int i2c_read (int devNum, int devAddr, int addr, uint8_t *buffer, int numBytes); 63 | int i2c_readRaw (int devNum, int devAddr, uint8_t *buffer, int numBytes); 64 | int i2c_readByte (int devNum, int devAddr, int addr, int *val); 65 | 66 | int i2c_readMultiByteAddr (int devNum, int devAddr, uint8_t *addrBuffer, int addrLength, uint8_t *buffer, int dataLength); 67 | int i2c_writeMultiByteAddr (int devNum, int devAddr, uint8_t *addrBuffer, int addrLength, uint8_t *buffer, int dataLength); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | #endif // _ONION_I2C_H_ 73 | -------------------------------------------------------------------------------- /include/onion-mcp23008-driver.h: -------------------------------------------------------------------------------- 1 | #ifndef _ONION_MCP23008_DRIVER_ 2 | #define _ONION_MCP23008_DRIVER_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | 10 | #define MCP23008_I2C_DEVICE_NUM (I2C_DEFAULT_ADAPTER) 11 | #define MCP23008_I2C_DEVICE_ADDR 0x20 12 | 13 | #define MCP23008_NUM_GPIOS 8 14 | 15 | // define register addresses 16 | #define MCP23008_REG_IODIR 0x00 17 | #define MCP23008_REG_IPOL 0x01 18 | #define MCP23008_REG_GPINTEN 0x02 19 | #define MCP23008_REG_DEFVAL 0x03 20 | #define MCP23008_REG_INTCON 0x04 21 | #define MCP23008_REG_IOCON 0x05 22 | #define MCP23008_REG_GPPU 0x06 23 | #define MCP23008_REG_INTF 0x07 24 | #define MCP23008_REG_INTCAP 0x08 25 | #define MCP23008_REG_GPIO 0x09 26 | #define MCP23008_REG_OLAT 0x0A 27 | 28 | 29 | // note on device addr and hw: 30 | // device addr is 0x20 when A[2:0] pins are driven to 0 31 | // 32 | // increment in binary when A[2:0] pins are driven: 33 | // A[2:0] device addr 34 | // 000 0x20 35 | // 001 0x21 36 | // 010 0x22 37 | // 011 0x23 38 | // 100 0x24 39 | // 101 0x25 40 | // 110 0x26 41 | // 111 0x27 42 | 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | // helper functions 49 | void _SetBit (int* regVal, int bitNum, int value); 50 | int _GetBit (int regVal, int bitNum); 51 | 52 | int _ValidateGpio (int gpio); 53 | 54 | 55 | int _SetReg (int devAddr, int addr, int val); 56 | int _ReadReg (int devAddr, int addr, int* val); 57 | 58 | int _SetRegBit (int devAddr, int addr, int bitNum, int bitVal); 59 | 60 | int _SetAttribute (int devAddr, int gpio, int addr, int val); 61 | int _GetAttribute (int devAddr, int gpio, int addr, int* val); 62 | 63 | 64 | // functions to set a single GPIO's attributes 65 | int mcp_setDirection (int devAddr, int gpio, int bInput); 66 | int mcp_setInputPolarity (int devAddr, int gpio, int bActiveLow); 67 | int mcp_setPullup (int devAddr, int gpio, int bPullUp); 68 | int mcp_setGpio (int devAddr, int gpio, int value); 69 | 70 | // functions to set attributes for all GPIOs 71 | // expecting 8-bit values 72 | int mcp_setAllDirection (int devAddr, int bInput); 73 | int mcp_setAllInputPolarity (int devAddr, int bActiveLow); 74 | int mcp_setAllPullup (int devAddr, int bPullUp); 75 | int mcp_setAllGpio (int devAddr, int value); 76 | 77 | // functions to read a single GPIO's attributes 78 | int mcp_getDirection (int devAddr, int gpio, int* bInput); 79 | int mcp_getInputPolarity (int devAddr, int gpio, int* bActiveLow); 80 | int mcp_getPullup (int devAddr, int gpio, int* bPullUp); 81 | int mcp_getGpio (int devAddr, int gpio, int* value); 82 | 83 | 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | #endif // _ONION_MCP23008_DRIVER_ -------------------------------------------------------------------------------- /include/pwm-exp.h: -------------------------------------------------------------------------------- 1 | #ifndef _PWM_EXP_H_ 2 | #define _PWM_EXP_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | 12 | 13 | 14 | #define PWM_I2C_DEVICE_NUM (I2C_DEFAULT_ADAPTER) 15 | #define PWM_I2C_DEVICE_ADDR 0x5a 16 | 17 | 18 | 19 | #define LED_FULL_VAL 0x1000 20 | 21 | #define PWM_FREQUENCY_DEFAULT 50.0f 22 | #define PWM_FREQUENCY_MIN 24.0f 23 | #define PWM_FREQUENCY_MAX 1526.0f 24 | 25 | #define OSCILLATOR_CLOCK 25000000.0f 26 | #define PULSE_TOTAL_COUNT 4096 27 | 28 | #define PRESCALE_MIN_VALUE 0x03 29 | #define PRESCALE_MAX_VALUE 0xff 30 | 31 | #define PWM_EXP_REG_MODE1 0x00 32 | #define PWM_EXP_REG_MODE2 0x01 33 | #define PWM_EXP_REG_ADDR_PRESCALE 0xfe 34 | 35 | #define PWM_EXP_REG_MODE1_RESET 0x80 36 | #define PWM_EXP_REG_MODE1_SLEEP 0x10 37 | #define PWM_EXP_REG_MODE1_ALLCALL 0x01 38 | #define PWM_EXP_REG_MODE2_OUTDRV 0x04 39 | 40 | 41 | #define REG_OFFSET_BYTE0 0x0 42 | #define REG_OFFSET_BYTE1 0x1 43 | 44 | #define REG_OFFSET_ON_BYTES 0x0 45 | #define REG_OFFSET_OFF_BYTES 0x2 46 | 47 | #define PWM_EXP_NUM_CHANNELS 16 48 | 49 | // type definitions 50 | typedef enum e_PwmDriverAddr { 51 | PWM_EXP_REG_ADDR_DRIVER0 = 0x06, 52 | PWM_EXP_REG_ADDR_DRIVER1 = 0x0a, 53 | PWM_EXP_REG_ADDR_DRIVER2 = 0x0e, 54 | PWM_EXP_REG_ADDR_DRIVER3 = 0x12, 55 | PWM_EXP_REG_ADDR_DRIVER4 = 0x16, 56 | PWM_EXP_REG_ADDR_DRIVER5 = 0x1a, 57 | PWM_EXP_REG_ADDR_DRIVER6 = 0x1e, 58 | PWM_EXP_REG_ADDR_DRIVER7 = 0x22, 59 | PWM_EXP_REG_ADDR_DRIVER8 = 0x26, 60 | PWM_EXP_REG_ADDR_DRIVER9 = 0x2a, 61 | PWM_EXP_REG_ADDR_DRIVER10 = 0x2e, 62 | PWM_EXP_REG_ADDR_DRIVER11 = 0x32, 63 | PWM_EXP_REG_ADDR_DRIVER12 = 0x36, 64 | PWM_EXP_REG_ADDR_DRIVER13 = 0x3a, 65 | PWM_EXP_REG_ADDR_DRIVER14 = 0x3e, 66 | PWM_EXP_REG_ADDR_DRIVER15 = 0x42, 67 | PWM_EXP_REG_ADDR_DRIVER_ALL = 0xfa 68 | } ePwmDriverAddr; 69 | 70 | struct pwmSetup { 71 | int driverNum; 72 | int regOffset; 73 | 74 | int timeStart; 75 | int timeEnd; 76 | 77 | int prescale; 78 | }; 79 | 80 | 81 | #ifdef __cplusplus 82 | extern "C" { 83 | #endif 84 | 85 | // helper functions 86 | void _initPwmSetup (struct pwmSetup *obj); 87 | int _dutyToCount (float duty); 88 | 89 | int _getDriverRegisterOffset (int driverNum, int *addr); 90 | int _writeValue (int addr, int value); 91 | int _pwmSetTime (struct pwmSetup *setup); 92 | 93 | int _pwmSetReset (void); 94 | 95 | int _pwmGetSleepMode (int *bSleepMode); 96 | int _pwmSetSleepMode (int bSleepMode); 97 | 98 | 99 | void _pwmCalculate (float duty, float delay, struct pwmSetup *setup); 100 | 101 | 102 | // programming functions 103 | int pwmCheckInit (int *bInitialized); 104 | int pwmDriverInit (); 105 | 106 | int pwmDisableChip (); 107 | 108 | int pwmSetFrequency (float freq); 109 | int pwmSetupDriver (int driverNum, float duty, float delay); 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | #endif // _PWM_EXP_H_ -------------------------------------------------------------------------------- /include/relay-exp.h: -------------------------------------------------------------------------------- 1 | #ifndef _RELAY_EXP_H_ 2 | #define _RELAY_EXP_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #define RELAY_EXP_ADDR_SWITCH_NUM 3 12 | #define RELAY_EXP_ADDR_SWITCH_DEFAULT_VAL "000" 13 | #define RELAY_EXP_ADDR_DEFAULT 0x27 14 | 15 | // type definitions 16 | typedef enum e_RelayDriverChannels { 17 | RELAY_EXP_CHANNEL0 = 0, 18 | RELAY_EXP_CHANNEL1, 19 | RELAY_EXP_CHANNEL2, 20 | RELAY_EXP_CHANNEL3, 21 | RELAY_EXP_CHANNEL4, 22 | RELAY_EXP_CHANNEL5, 23 | RELAY_EXP_CHANNEL6, 24 | RELAY_EXP_CHANNEL7, 25 | RELAY_EXP_NUM_CHANNELS, 26 | } eRelayDriverChannels; 27 | 28 | 29 | #define RELAY_EXP_NUM_CHANNELS_DEFAULT (RELAY_EXP_CHANNEL1+1) 30 | #define RELAY_EXP_NUM_CHANNELS_EXTENDED (RELAY_EXP_NUM_CHANNELS) 31 | 32 | 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | int relayDriverInit (int addr); 39 | int relayCheckInit (int addr, int *bInitialized); 40 | 41 | int relayReadChannel (int addr, int channel, int *state); 42 | 43 | int relaySetChannel (int addr, int channel, int state); 44 | int relaySetAllChannels (int addr, int state); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | #endif // _RELAY_EXP_H_ -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | # main compiler 3 | CC := gcc 4 | # CC := clang --analyze # and comment out the linker last line for sanity 5 | 6 | # define the directories 7 | SRCDIR := src 8 | INCDIR := include 9 | BUILDDIR := build 10 | BINDIR := bin 11 | LIBDIR := lib 12 | 13 | 14 | # define common variables 15 | SRCEXT := c 16 | SOURCES := $(shell find $(SRCDIR) -maxdepth 1 -type f \( -iname "*.$(SRCEXT)" ! -iname "*main-*.$(SRCEXT)" \) ) 17 | OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o)) 18 | CFLAGS := -g -fPIC # -Wall 19 | INC := $(shell find $(INCDIR) -maxdepth 1 -type d -exec echo -I {} \;) 20 | 21 | 22 | PYLIBDIR := lib/python$(PYTHON_VERSION) 23 | PYINC := "-I/usr/include/python$(PYTHON_VERSION)" 24 | INC += $(PYINC) 25 | 26 | # define specific binaries to create 27 | # C libraries 28 | LIBD := liboniondebug 29 | SOURCE_LIBD := src/lib/onion-debug.$(SRCEXT) 30 | OBJECT_LIBD := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_LIBD:.$(SRCEXT)=.o)) 31 | TARGET_LIBD := $(LIBDIR)/$(LIBD).so 32 | 33 | LIB0 := libonioni2c 34 | SOURCE_LIB0 := src/lib/onion-i2c.$(SRCEXT) 35 | OBJECT_LIB0 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_LIB0:.$(SRCEXT)=.o)) 36 | TARGET_LIB0 := $(LIBDIR)/$(LIB0).so 37 | LIB_LIB0 := -L$(LIBDIR) -loniondebug 38 | 39 | LIB1 := libonionmcp23008 40 | SOURCE_LIB1 := src/lib/onion-mcp23008-driver.$(SRCEXT) 41 | OBJECT_LIB1 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_LIB1:.$(SRCEXT)=.o)) 42 | TARGET_LIB1 := $(LIBDIR)/$(LIB1).so 43 | LIB_LIB1 := -L$(LIBDIR) -loniondebug -lonioni2c 44 | 45 | LIB2 := libonionpwmexp 46 | SOURCE_LIB2 := src/pwm-exp.$(SRCEXT) 47 | OBJECT_LIB2 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_LIB2:.$(SRCEXT)=.o)) 48 | TARGET_LIB2 := $(LIBDIR)/$(LIB2).so 49 | LIB_LIB2 := -L$(LIBDIR) -loniondebug -lonioni2c 50 | 51 | LIB3 := libonionrelayexp 52 | SOURCE_LIB3 := src/relay-exp.$(SRCEXT) 53 | OBJECT_LIB3 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_LIB3:.$(SRCEXT)=.o)) 54 | TARGET_LIB3 := $(LIBDIR)/$(LIB3).so 55 | LIB_LIB3 := -L$(LIBDIR) -loniondebug -lonioni2c -lonionmcp23008 56 | 57 | LIB4 := libonionoledexp 58 | SOURCE_LIB4 := src/oled-exp.$(SRCEXT) 59 | OBJECT_LIB4 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_LIB4:.$(SRCEXT)=.o)) 60 | TARGET_LIB4 := $(LIBDIR)/$(LIB4).so 61 | LIB_LIB4 := -L$(LIBDIR) -loniondebug -lonioni2c 62 | 63 | 64 | # C applications 65 | APP0 := pwm-exp 66 | SOURCE_APP0 := $(SRCDIR)/main-$(APP0).$(SRCEXT) 67 | OBJECT_APP0 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_APP0:.$(SRCEXT)=.o)) 68 | LIB_APP0 := -L$(LIBDIR) -loniondebug -lonioni2c -lonionpwmexp 69 | TARGET_APP0 := $(BINDIR)/$(APP0) 70 | 71 | APP1 := relay-exp 72 | SOURCE_APP1 := $(SRCDIR)/main-$(APP1).$(SRCEXT) 73 | OBJECT_APP1 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_APP1:.$(SRCEXT)=.o)) 74 | LIB_APP1 := -L$(LIBDIR) -loniondebug -lonioni2c -lonionmcp23008 -lonionrelayexp 75 | TARGET_APP1 := $(BINDIR)/$(APP1) 76 | 77 | APP2 := oled-exp 78 | SOURCE_APP2 := $(SRCDIR)/main-$(APP2).$(SRCEXT) $(SRCDIR)/$(APP2).$(SRCEXT) 79 | OBJECT_APP2 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_APP2:.$(SRCEXT)=.o)) 80 | LIB_APP2 := -L$(LIBDIR) -loniondebug -lonioni2c -lonionoledexp 81 | TARGET_APP2 := $(BINDIR)/$(APP2) 82 | 83 | 84 | # Python Modules 85 | PYLIB00 := onionI2C 86 | SOURCE_PYLIB00 := src/python/onion-i2c-module.$(SRCEXT) 87 | OBJECT_PYLIB00 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_PYLIB00:.$(SRCEXT)=.o)) 88 | TARGET_PYLIB00 := $(PYLIBDIR)/$(PYLIB00).so 89 | LIB_PYLIB00 := -L$(LIBDIR) -loniondebug -lonioni2c -lpython$(PYTHON_VERSION) 90 | 91 | PYLIB0 := pwmExp 92 | SOURCE_PYLIB0 := src/python/pwm-exp-module.$(SRCEXT) 93 | OBJECT_PYLIB0 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_PYLIB0:.$(SRCEXT)=.o)) 94 | TARGET_PYLIB0 := $(PYLIBDIR)/$(PYLIB0).so 95 | LIB_PYLIB0 := -L$(LIBDIR) -loniondebug -lonioni2c -lonionpwmexp -lpython$(PYTHON_VERSION) 96 | 97 | PYLIB1 := relayExp 98 | SOURCE_PYLIB1 := src/python/relay-exp-module.$(SRCEXT) 99 | OBJECT_PYLIB1 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_PYLIB1:.$(SRCEXT)=.o)) 100 | TARGET_PYLIB1 := $(PYLIBDIR)/$(PYLIB1).so 101 | LIB_PYLIB1 := -L$(LIBDIR) -loniondebug -lonioni2c -lonionmcp23008 -lonionrelayexp -lpython$(PYTHON_VERSION) 102 | 103 | PYLIB2 := oledExp 104 | SOURCE_PYLIB2 := src/python/oled-exp-module.$(SRCEXT) 105 | OBJECT_PYLIB2 := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCE_PYLIB2:.$(SRCEXT)=.o)) 106 | TARGET_PYLIB2 := $(PYLIBDIR)/$(PYLIB2).so 107 | LIB_PYLIB2 := -L$(LIBDIR) -loniondebug -lonioni2c -lonionoledexp -lpython$(PYTHON_VERSION) 108 | 109 | 110 | 111 | all: validate resp $(TARGET_LIBD) $(TARGET_LIB0) $(TARGET_LIB1) $(TARGET_LIB2) $(TARGET_LIB3) $(TARGET_LIB4) $(TARGET_APP0) $(TARGET_APP1) $(TARGET_APP2) $(TARGET_PYLIB00) $(TARGET_PYLIB0) $(TARGET_PYLIB1) $(TARGET_PYLIB2) 112 | 113 | 114 | # libraries 115 | $(TARGET_LIBD): $(OBJECT_LIBD) 116 | @echo " Compiling $@" 117 | @mkdir -p $(LIBDIR) 118 | $(CC) -shared -o $@ $^ $(LIB_LIBD) 119 | 120 | $(TARGET_LIB0): $(OBJECT_LIB0) 121 | @echo " Compiling $@" 122 | @mkdir -p $(LIBDIR) 123 | $(CC) -shared -o $@ $^ $(LIB_LIB0) 124 | 125 | $(TARGET_LIB1): $(OBJECT_LIB1) 126 | @echo " Compiling $@" 127 | @mkdir -p $(LIBDIR) 128 | $(CC) -shared -o $@ $^ $(LIB_LIB1) 129 | 130 | $(TARGET_LIB2): $(OBJECT_LIB2) 131 | @echo " Compiling $@" 132 | @mkdir -p $(LIBDIR) 133 | $(CC) -shared -o $@ $^ $(LIB_LIB2) 134 | 135 | $(TARGET_LIB3): $(OBJECT_LIB3) 136 | @echo " Compiling $@" 137 | @mkdir -p $(LIBDIR) 138 | $(CC) -shared -o $@ $^ $(LIB_LIB3) 139 | 140 | $(TARGET_LIB4): $(OBJECT_LIB4) 141 | @echo " Compiling $@" 142 | @mkdir -p $(LIBDIR) 143 | $(CC) -shared -o $@ $^ $(LIB_LIB4) 144 | 145 | 146 | # application binaries 147 | $(TARGET_APP0): $(OBJECT_APP0) 148 | @echo " Compiling $(APP0)" 149 | @mkdir -p $(BINDIR) 150 | @echo " Linking..." 151 | $(CC) $^ $(CFLAGS) $(LDFLAGS) -o $(TARGET_APP0) $(LIB) $(LIB_APP0) 152 | 153 | $(TARGET_APP1): $(OBJECT_APP1) 154 | @echo " Compiling $(APP1)" 155 | @mkdir -p $(BINDIR) 156 | @echo " Linking..." 157 | $(CC) $^ $(CFLAGS) $(LDFLAGS) -o $(TARGET_APP1) $(LIB) $(LIB_APP1) 158 | 159 | $(TARGET_APP2): $(OBJECT_APP2) 160 | @echo " Compiling $(APP2)" 161 | @mkdir -p $(BINDIR) 162 | @echo " Linking..." 163 | $(CC) $^ $(CFLAGS) $(LDFLAGS) -o $(TARGET_APP2) $(LIB) $(LIB_APP2) 164 | 165 | 166 | $(TARGET_PYLIB00): $(OBJECT_PYLIB00) 167 | @echo " Compiling $@" 168 | @mkdir -p $(PYLIBDIR) 169 | $(CC) -shared -o $@ $^ $(LIB_PYLIB00) 170 | 171 | $(TARGET_PYLIB0): $(OBJECT_PYLIB0) 172 | @echo " Compiling $@" 173 | @mkdir -p $(PYLIBDIR) 174 | $(CC) -shared -o $@ $^ $(LIB_PYLIB0) 175 | 176 | $(TARGET_PYLIB1): $(OBJECT_PYLIB1) 177 | @echo " Compiling $@" 178 | @mkdir -p $(PYLIBDIR) 179 | $(CC) -shared -o $@ $^ $(LIB_PYLIB1) 180 | 181 | $(TARGET_PYLIB2): $(OBJECT_PYLIB2) 182 | @echo " Compiling $@" 183 | @mkdir -p $(PYLIBDIR) 184 | $(CC) -shared -o $@ $^ $(LIB_PYLIB2) 185 | 186 | 187 | # generic: build any object file required 188 | $(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT) 189 | @mkdir -p $(dir $@) 190 | @echo " $(CC) $(CFLAGS) $(INC) -c -o $@ $<"; $(CC) $(CFLAGS) $(INC) -c -o $@ $< 191 | 192 | clean: 193 | @echo " Cleaning..."; 194 | $(RM) -r $(BUILDDIR) $(BINDIR) $(LIBDIR) 195 | 196 | bla: 197 | @echo "$(BLA)" 198 | @echo "TARGET_APP0: $(APP0) $(SOURCE_APP0) $(OBJECT_APP0) $(TARGET_APP0)" 199 | @echo "Sources: $(SOURCES)" 200 | @echo "Objects: $(OBJECTS)" 201 | 202 | resp: 203 | @echo "CC: $(CC)" 204 | @echo "CFLAGS: $(CFLAGS)" 205 | @echo "LDFLAGS: $(LDFLAGS)" 206 | @echo "LIB: $(LIB)" 207 | 208 | validate: 209 | ifeq ($(PYTHON_VERSION),) 210 | $(error "PYTHON_VERSION variable is not set") 211 | else 212 | $(info "Using PYTHON_VERSION $(PYTHON_VERSION)") 213 | endif 214 | 215 | # Tests 216 | tester: 217 | $(CC) $(CFLAGS) test/tester.cpp $(INC) $(LIB) -o bin/tester 218 | 219 | # Spikes 220 | #ticket: 221 | # $(CC) $(CFLAGS) spikes/ticket.cpp $(INC) $(LIB) -o bin/ticket 222 | 223 | .PHONY: clean 224 | -------------------------------------------------------------------------------- /rpcd/i2c-exp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # include the Onion sh lib 4 | . /usr/lib/onion/lib.sh 5 | 6 | 7 | ##### pwm-exp ##### 8 | # function to set a pwm channel with duty cycle 9 | PwmExpSet () { 10 | # select the arguments object 11 | json_select params 12 | # parse the arguments 13 | json_get_var channel "channel" 14 | json_get_var duty "duty" 15 | json_get_var delay "delay" 16 | json_get_var freq "frequency" 17 | if [ "$freq" != "" ]; then 18 | freq="-f $freq" 19 | fi 20 | 21 | # check arguments 22 | if [ "$channel" == "" ]; then 23 | ret="missing channel argument" 24 | elif [ "$duty" == "" ]; then 25 | ret="missing duty argument" 26 | else 27 | ret=$(/usr/sbin/pwm-exp -q $freq $channel $duty $delay) 28 | fi 29 | Log "pwm-exp: set $freq $channel $duty $delay" 30 | 31 | echo "$ret" 32 | } 33 | 34 | # function to set a pwm channel with pulse width and period 35 | PwmExpSetPeriod () { 36 | # select the arguments object 37 | json_select params 38 | # parse the arguments object 39 | json_get_var channel "channel" 40 | json_get_var pulse "pulse" 41 | json_get_var period "period" 42 | 43 | # check arguments 44 | if [ "$channel" == "" ]; then 45 | ret="missing channel argument" 46 | elif [ "$pulse" == "" ]; then 47 | ret="missing pulse argument" 48 | elif [ "$period" == "" ]; then 49 | ret="missing period argument" 50 | else 51 | ret=$(/usr/sbin/pwm-exp -q -p $channel $pulse $period) 52 | fi 53 | Log "pwm-exp: set -p $channel $pulse $period" 54 | 55 | echo "$ret" 56 | } 57 | 58 | # main pwm-exp function 59 | PwmExpMain () { 60 | # read the command 61 | json_get_var cmd "command" 62 | 63 | # specific commands 64 | case "$cmd" in 65 | init) 66 | ret=$(/usr/sbin/pwm-exp -q -i) 67 | Log "pwm-exp: init, ret is $ret" 68 | ;; 69 | sleep) 70 | ret=$(/usr/sbin/pwm-exp -q -s) 71 | Log "pwm-exp: sleep, ret is $ret" 72 | ;; 73 | set) 74 | ret=$(PwmExpSet) 75 | ;; 76 | set-period) 77 | ret=$(PwmExpSetPeriod) 78 | ;; 79 | esac 80 | 81 | # print return message 82 | if [ "$ret" == "" ]; then 83 | echo '{"status":"success"}' 84 | else 85 | echo '{"status":"failure", "error":"'"$ret"'"}' 86 | fi 87 | } 88 | 89 | 90 | ##### relay-exp ##### 91 | # main relay-exp function 92 | RelayExpMain () { 93 | # read the command 94 | json_get_var cmd "command" 95 | 96 | # select the arguments object 97 | json_select params 98 | # parse the arguments object 99 | json_get_var channel "channel" 100 | # parse and process the address 101 | json_get_var addr "address" 102 | if [ "$addr" != "" ]; then 103 | local check=$(echo $addr | grep '^0x') 104 | Log "address check: $check" 105 | if [ "$check" != "" ]; then 106 | # address is actual hex address (ex: 0x27) 107 | addr="-a $addr" 108 | else 109 | # address is switch binary (ex: 110) 110 | addr="-s $addr" 111 | fi 112 | fi 113 | #Log "relay-exp: " 114 | 115 | # carry out the commands 116 | if [ "$cmd" == "set" ] 117 | then 118 | # SET the relay state 119 | json_get_var state "state" 120 | 121 | # check arguments 122 | if [ "$channel" == "" ]; then 123 | ret="missing channel argument" 124 | elif [ "$state" == "" ]; then 125 | ret="missing state argument" 126 | else 127 | ret=$(/usr/sbin/relay-exp -q $addr $channel $state) 128 | fi 129 | Log "relay-exp: set $addr $channel $state" 130 | elif [ "$cmd" == "get" ] 131 | then 132 | # GET the relay state 133 | # check arguments 134 | if [ "$channel" == "" ]; then 135 | ret="missing channel argument" 136 | else 137 | ret=$(/usr/sbin/relay-exp $addr read $channel) 138 | errorCheck=$(echo $ret | grep -i 'error') 139 | Log "relay-exp: get $addr $channel" 140 | 141 | statusOn=$(echo $ret | grep 'ON') 142 | statusOff=$(echo $ret | grep 'OFF') 143 | 144 | if [ "$errorCheck" != "" ]; then 145 | ret="$errorCheck" # return the error 146 | elif [ "$statusOn" != "" ]; then 147 | state=1 148 | ret="" # clear ret to indicate no error 149 | elif [ "$statusOff" != "" ]; then 150 | state=0 151 | ret="" # clear ret to indicate no error 152 | fi 153 | fi 154 | else 155 | ret="invalid command" 156 | fi 157 | 158 | # print return message 159 | if [ "$ret" == "" ]; then 160 | echo '{"status":"success", "channel":"'"$channel"'", "state":"'"$state"'"}' 161 | else 162 | echo '{"status":"failure", "error":"'"$ret"'"}' 163 | fi 164 | } 165 | 166 | 167 | ##### oled-exp ##### 168 | # main oled-exp function 169 | OledExpMain () { 170 | # read the command 171 | json_get_var cmd "command" 172 | 173 | # only command is 'set' 174 | if [ "$cmd" == "set" ] 175 | then 176 | # parse the options 177 | json_get_var opts "option" 178 | if [ "$opts" != "" ]; then 179 | opts="-$opts" 180 | fi 181 | # parse the arguments object 182 | json_get_keys param "params" 183 | if [ "$param" != "" ]; then 184 | cmds=$(_ParseArgumentsObject "nodash") 185 | fi 186 | 187 | Log "relay-exp: opts: $opts, cmds: $cmds" 188 | Log "/usr/sbin/oled-exp -q $opts $cmds" 189 | 190 | # run the command 191 | # need the eval command to properly evaluate any double quotes in the $cmds variable 192 | ret=$(eval "/usr/sbin/oled-exp -q $opts $cmds") 193 | else 194 | ret="invalid command" 195 | fi 196 | 197 | # print return message 198 | if [ "$ret" == "" ]; then 199 | cmds=$(echo $cmds | sed -e "s/\"/'/g") 200 | echo '{"status":"success", "options":"'"$opts"'", "commands":"'"$cmds"'"}' 201 | else 202 | echo '{"status":"failure", "error":"'"$ret"'"}' 203 | fi 204 | } 205 | 206 | 207 | ######################## 208 | ##### Main Program ##### 209 | 210 | cmdPwmExp="pwm-exp" 211 | cmdRelayExp="relay-exp" 212 | cmdOledExp="oled-exp" 213 | cmdStatus="status" 214 | 215 | jsonPwmExp='"'"$cmdPwmExp"'": { "command":"cmd", "params": {"key": "value" } }' 216 | jsonRelayExp='"'"$cmdRelayExp"'": { "command":"cmd", "params": {"key": "value" } }' 217 | jsonOledExp='"'"$cmdOledExp"'": { "command":"cmd", "option":"opt", "params": {"key": "value" } }' 218 | jsonStatus='"'"$cmdStatus"'": { }' 219 | 220 | 221 | case "$1" in 222 | list) 223 | echo "{ $jsonPwmExp, $jsonRelayExp, $jsonOledExp, $jsonStatus }" 224 | ;; 225 | call) 226 | Log "Function: call, Method: $2" 227 | 228 | case "$2" in 229 | $cmdPwmExp) 230 | # read the json arguments 231 | read input; 232 | Log "Json argument: $input" 233 | 234 | # parse the json 235 | json_load "$input" 236 | 237 | # parse the json and run the function 238 | PwmExpMain 239 | ;; 240 | $cmdRelayExp) 241 | # read the json arguments 242 | read input; 243 | Log "Json argument: $input" 244 | 245 | # parse the json 246 | json_load "$input" 247 | 248 | # parse the json and run the function 249 | RelayExpMain 250 | ;; 251 | $cmdOledExp) 252 | # read the json arguments 253 | read input; 254 | Log "Json argument: $input" 255 | 256 | # parse the json 257 | json_load "$input" 258 | 259 | # parse the json and run the function 260 | OledExpMain 261 | ;; 262 | $cmdStatus) 263 | # dummy call for now 264 | echo '{"status":"good"}' 265 | ;; 266 | esac 267 | ;; 268 | esac 269 | 270 | 271 | -------------------------------------------------------------------------------- /src/lib/onion-debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | void onionSetVerbosity (int verbosityLevel) 5 | { 6 | onionVerbosityLevel = verbosityLevel; 7 | } 8 | 9 | int onionGetVerbosity () 10 | { 11 | return (onionVerbosityLevel); 12 | } 13 | 14 | void onionPrint (int severity, const char* msg, ...) 15 | { 16 | va_list argptr; 17 | 18 | if (onionVerbosityLevel >= severity) { 19 | va_start(argptr, msg); 20 | vprintf(msg, argptr); 21 | va_end(argptr); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/onion-i2c.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | // get a file handle to the device 5 | int _i2c_getFd(int adapterNum, int *devHandle) 6 | { 7 | int status; 8 | char pathname[255]; 9 | 10 | // define the path to open 11 | status = snprintf(pathname, sizeof(pathname), I2C_DEV_PATH, adapterNum); 12 | 13 | // check the filename 14 | if (status < 0 || status >= sizeof(pathname)) { 15 | // add errno 16 | return EXIT_FAILURE; 17 | } 18 | 19 | // create a file descriptor for the I2C bus 20 | #ifdef I2C_ENABLED 21 | *devHandle = open(pathname, O_RDWR); 22 | #else 23 | *devHandle = 0; 24 | #endif 25 | 26 | // check the defvice handle 27 | if (*devHandle < 0) { 28 | // add errno 29 | return EXIT_FAILURE; 30 | } 31 | 32 | return EXIT_SUCCESS; 33 | } 34 | 35 | // release the device file handle 36 | int _i2c_releaseFd(int devHandle) 37 | { 38 | #ifdef I2C_ENABLED 39 | if ( close(devHandle) < 0 ) { 40 | return EXIT_FAILURE; 41 | } 42 | #endif 43 | 44 | return EXIT_SUCCESS; 45 | } 46 | 47 | // set the device address 48 | int _i2c_setDevice(int devHandle, int addr) 49 | { 50 | #ifdef I2C_ENABLED 51 | // set to 7-bit addr 52 | if ( ioctl(devHandle, I2C_TENBIT, 0) < 0 ) { 53 | return EXIT_FAILURE; 54 | } 55 | 56 | // set the address 57 | if ( ioctl(devHandle, I2C_SLAVE, addr) < 0 ) { 58 | return EXIT_FAILURE; 59 | } 60 | #endif 61 | 62 | return EXIT_SUCCESS; 63 | } 64 | 65 | // set the 10bit device address 66 | int _i2c_setDevice10bit(int devHandle, int addr) 67 | { 68 | #ifdef I2C_ENABLED 69 | // set to 10-bit addr 70 | if ( ioctl(devHandle, I2C_TENBIT, 1) < 0 ) { 71 | return EXIT_FAILURE; 72 | } 73 | 74 | // set the address 75 | if ( _i2c_setDevice(devHandle, addr) != EXIT_SUCCESS ) { 76 | return EXIT_FAILURE; 77 | } 78 | #endif 79 | 80 | return EXIT_SUCCESS; 81 | } 82 | 83 | // generic function to write a buffer to the i2c bus 84 | int _i2c_writeBuffer(int devNum, int devAddr, uint8_t *buffer, int size) 85 | { 86 | int status; 87 | int fd, index; 88 | 89 | // open the file handle 90 | status = _i2c_getFd(devNum, &fd); 91 | 92 | // set the device address 93 | if ( status == EXIT_SUCCESS ) { 94 | status = _i2c_setDevice(fd, devAddr); 95 | } 96 | 97 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "%s writing buffer:\n", I2C_PRINT_BANNER); 98 | for (index = 0; index < size; index++) { 99 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "\tbuffer[%d]: 0x%02x\n", index, buffer[index]); 100 | } 101 | 102 | // perform the write 103 | if ( status == EXIT_SUCCESS ) { 104 | #ifdef I2C_ENABLED 105 | // write to the i2c device 106 | status = write(fd, buffer, size); 107 | if (status != size) { 108 | onionPrint(ONION_SEVERITY_FATAL, "%s write issue for register 0x%02x, errno is %d: %s\n", I2C_PRINT_BANNER, buffer[0], errno, strerror(errno) ); 109 | status = EXIT_FAILURE; 110 | } 111 | else { 112 | status = EXIT_SUCCESS; 113 | } 114 | #endif 115 | } 116 | 117 | // release the device file handle 118 | status |= _i2c_releaseFd(fd); 119 | 120 | return (status); 121 | } 122 | 123 | // generic function to write a buffer to the i2c bus 124 | int i2c_writeBuffer(int devNum, int devAddr, int addr, uint8_t *buffer, int size) 125 | { 126 | int status; 127 | uint8_t *bufferNew; 128 | 129 | // allocate the new buffer 130 | size++; // adding addr to buffer 131 | bufferNew = malloc( size * sizeof *bufferNew ); 132 | 133 | // add the address to the data buffer 134 | bufferNew[0] = addr; 135 | memcpy( &bufferNew[1], &buffer[0], (size-1) * sizeof *buffer ); 136 | 137 | // perform the write 138 | status = _i2c_writeBuffer(devNum, devAddr, bufferNew, size); 139 | 140 | // free the allocated memory 141 | free(bufferNew); 142 | 143 | return (status); 144 | } 145 | 146 | // generic function to write a buffer to the i2c bus (no in-device address 147 | int i2c_writeBufferRaw(int devNum, int devAddr, uint8_t *buffer, int size) 148 | { 149 | return _i2c_writeBuffer(devNum, devAddr, buffer, size); 150 | } 151 | 152 | // write n bytes to the i2c bus 153 | int i2c_write(int devNum, int devAddr, int addr, int val) 154 | { 155 | int status; 156 | int size, tmp, index; 157 | uint8_t buffer[I2C_BUFFER_SIZE]; 158 | 159 | //// buffer setup 160 | // clear the buffer 161 | memset( buffer, 0, sizeof(buffer) ); 162 | // push the address and data values into the buffer 163 | buffer[0] = (addr & 0xff); 164 | buffer[1] = (val & 0xff); 165 | size = 2; 166 | 167 | // if value is more than 1-byte, add to the buffer 168 | tmp = (val >> 8); // start with byte 1 169 | index = 2; 170 | while (tmp > 0x00) { 171 | buffer[index] = (uint8_t)(tmp & 0xff); 172 | 173 | tmp = tmp >> 8; // advance the tmp data by a byte 174 | index++; // increment the index 175 | 176 | size++; // increase the size 177 | } 178 | 179 | onionPrint(ONION_SEVERITY_DEBUG, "%s Writing to device 0x%02x: addr = 0x%02x, data = 0x%02x (data size: %d)\n", I2C_PRINT_BANNER, devAddr, addr, val, (size-1) ); 180 | 181 | // write the buffer 182 | status = _i2c_writeBuffer(devNum, devAddr, buffer, size); 183 | 184 | return (status); 185 | } 186 | 187 | // write a specified number of bytes to the i2c bus 188 | int i2c_writeBytes(int devNum, int devAddr, int addr, int val, int numBytes) 189 | { 190 | int status; 191 | int size, index; 192 | uint8_t buffer[I2C_BUFFER_SIZE]; 193 | 194 | //// buffer setup 195 | // clear the buffer 196 | memset( buffer, 0, sizeof(buffer) ); 197 | // push the address and data values into the buffer 198 | buffer[0] = (addr & 0xff); 199 | size = 1; 200 | 201 | // add all data bytes to buffer 202 | index = 1; 203 | for (index = 0; index < numBytes; index++) { 204 | buffer[index+1] = (uint8_t)( (val >> (8*index)) & 0xff ); 205 | 206 | size++; // increase the size 207 | } 208 | 209 | onionPrint(ONION_SEVERITY_DEBUG, "%s Writing to device 0x%02x: addr = 0x%02x, data = 0x%02x (data size: %d)\n", I2C_PRINT_BANNER, devAddr, addr, val, (size-1) ); 210 | 211 | // write the buffer 212 | status = _i2c_writeBuffer(devNum, devAddr, buffer, size); 213 | 214 | return (status); 215 | } 216 | 217 | // read a byte from the i2c bus 218 | int i2c_read(int devNum, int devAddr, int addr, uint8_t *buffer, int numBytes) 219 | { 220 | int status, size, index; 221 | int fd; 222 | 223 | onionPrint(ONION_SEVERITY_DEBUG, "%s Reading %d byte%s from device 0x%02x: addr = 0x%02x", I2C_PRINT_BANNER, numBytes, (numBytes > 1 ? "s": ""), devAddr, addr); 224 | 225 | // open the device file handle 226 | status = _i2c_getFd(devNum, &fd); 227 | 228 | // set the device address 229 | if ( status == EXIT_SUCCESS ) { 230 | status = _i2c_setDevice(fd, devAddr); 231 | } 232 | 233 | // perform the read 234 | if ( status == EXIT_SUCCESS ) { 235 | //// set addr 236 | // clear the buffer 237 | memset( buffer, 0, numBytes ); 238 | // push the address and data values into the buffer 239 | buffer[0] = (addr & 0xff); 240 | size = 1; 241 | 242 | #ifdef I2C_ENABLED 243 | // write to the i2c device 244 | status = write(fd, buffer, size); 245 | if (status != size) { 246 | onionPrint(ONION_SEVERITY_FATAL, "%s write issue for register 0x%02x, errno is %d: %s\n", I2C_PRINT_BANNER, addr, errno, strerror(errno) ); 247 | } 248 | #endif 249 | 250 | //// read data 251 | // clear the buffer 252 | memset( buffer, 0, numBytes ); 253 | 254 | #ifdef I2C_ENABLED 255 | // read from the i2c device 256 | size = numBytes; 257 | status = read(fd, buffer, size); 258 | if (status != size) { 259 | onionPrint(ONION_SEVERITY_FATAL, "%s read issue for register 0x%02x, errno is %d: %s\n", I2C_PRINT_BANNER, addr, errno, strerror(errno) ); 260 | status = EXIT_FAILURE; 261 | } 262 | else { 263 | status = EXIT_SUCCESS; 264 | } 265 | #else 266 | buffer[0] = 0x0; 267 | size = 1; 268 | /* 269 | // for debug 270 | printf("Setting buffer... it has length of %d\n", strlen(buffer) ); 271 | buffer[0] = 0x34; 272 | buffer[1] = 0x12; 273 | size = 2; 274 | printf("Done setting buffer... it has length of %d\n", strlen(buffer) ); 275 | printf("size is %d\n", size);*/ 276 | #endif 277 | 278 | //// print the data 279 | onionPrint(ONION_SEVERITY_DEBUG, "\tread %d byte%s, value: 0x", size, (size > 1 ? "s" : "") ); 280 | 281 | for (index = (size-1); index >= 0; index--) { 282 | onionPrint(ONION_SEVERITY_DEBUG, "%02x", (buffer[index] & 0xff) ); 283 | } 284 | onionPrint(ONION_SEVERITY_DEBUG, "\n"); 285 | } 286 | 287 | // release the device file handle 288 | status |= _i2c_releaseFd(fd); 289 | 290 | return (status); 291 | } 292 | 293 | // read a byte from the i2c bus 294 | int i2c_readRaw(int devNum, int devAddr, uint8_t *buffer, int numBytes) 295 | { 296 | int status, size, index; 297 | int fd; 298 | 299 | onionPrint(ONION_SEVERITY_DEBUG, "%s Reading %d byte%s from device 0x%02x", I2C_PRINT_BANNER, numBytes, (numBytes > 1 ? "s": ""), devAddr); 300 | 301 | // open the device file handle 302 | status = _i2c_getFd(devNum, &fd); 303 | 304 | // set the device address 305 | if ( status == EXIT_SUCCESS ) { 306 | status = _i2c_setDevice(fd, devAddr); 307 | } 308 | 309 | // perform the read 310 | if ( status == EXIT_SUCCESS ) { 311 | //// read data 312 | // clear the buffer 313 | memset( buffer, 0, numBytes ); 314 | 315 | #ifdef I2C_ENABLED 316 | // read from the i2c device 317 | size = numBytes; 318 | status = read(fd, buffer, size); 319 | if (status != size) { 320 | onionPrint(ONION_SEVERITY_FATAL, "%s read issue, errno is %d: %s\n", I2C_PRINT_BANNER, errno, strerror(errno) ); 321 | status = EXIT_FAILURE; 322 | } 323 | else { 324 | status = EXIT_SUCCESS; 325 | } 326 | #else 327 | buffer[0] = 0x0; 328 | size = 1; 329 | /* 330 | // for debug 331 | printf("Setting buffer... it has length of %d\n", strlen(buffer) ); 332 | buffer[0] = 0x34; 333 | buffer[1] = 0x12; 334 | size = 2; 335 | printf("Done setting buffer... it has length of %d\n", strlen(buffer) ); 336 | printf("size is %d\n", size);*/ 337 | #endif 338 | 339 | //// print the data 340 | onionPrint(ONION_SEVERITY_DEBUG, "\tread %d byte%s, value: 0x", size, (size > 1 ? "s" : "") ); 341 | 342 | for (index = (size-1); index >= 0; index--) { 343 | onionPrint(ONION_SEVERITY_DEBUG, "%02x", (buffer[index] & 0xff) ); 344 | } 345 | onionPrint(ONION_SEVERITY_DEBUG, "\n"); 346 | } 347 | 348 | // release the device file handle 349 | status |= _i2c_releaseFd(fd); 350 | 351 | return (status); 352 | } 353 | 354 | // read a single byte from the i2c bus 355 | int i2c_readByte(int devNum, int devAddr, int addr, int *val) 356 | { 357 | int status; 358 | uint8_t buffer[I2C_BUFFER_SIZE]; 359 | 360 | status = i2c_read ( devNum, 361 | devAddr, 362 | addr, 363 | buffer, 364 | 1 365 | ); 366 | 367 | *val = (int)(buffer[0]); 368 | 369 | return (status); 370 | } 371 | 372 | // read n-bytes from the i2c bus from a m-byte address 373 | int i2c_readMultiByteAddr (int devNum, int devAddr, uint8_t *addrBuffer, int addrLength, uint8_t *buffer, int dataLength) 374 | { 375 | int status, size, index; 376 | int fd; 377 | 378 | // onionPrint(ONION_SEVERITY_DEBUG, "%s Reading %d byte%s from device 0x%02x: addr = 0x%02x", I2C_PRINT_BANNER, dataLength, (dataLength > 1 ? "s": ""), devAddr, addr); 379 | 380 | 381 | // open the device file handle 382 | status = _i2c_getFd(devNum, &fd); 383 | 384 | // set the device address 385 | if ( status == EXIT_SUCCESS ) { 386 | status = _i2c_setDevice(fd, devAddr); 387 | } 388 | 389 | // perform the read 390 | if ( status == EXIT_SUCCESS ) { 391 | #ifdef I2C_ENABLED 392 | if (addrLength > 0) { 393 | // write to the i2c device 394 | status = write(fd, addrBuffer, addrLength); 395 | if (status != addrLength) { 396 | onionPrint(ONION_SEVERITY_FATAL, "%s write issue, errno is %d: %s\n", I2C_PRINT_BANNER, errno, strerror(errno) ); 397 | } 398 | } 399 | #endif 400 | 401 | //// read data 402 | // clear the buffer 403 | memset( buffer, 0, dataLength ); 404 | 405 | #ifdef I2C_ENABLED 406 | // read from the i2c device 407 | size = dataLength; 408 | status = read(fd, buffer, size); 409 | if (status != size) { 410 | onionPrint(ONION_SEVERITY_FATAL, "%s read issue, errno is %d: %s\n", I2C_PRINT_BANNER, errno, strerror(errno) ); 411 | status = EXIT_FAILURE; 412 | } 413 | else { 414 | status = EXIT_SUCCESS; 415 | } 416 | #endif 417 | } 418 | 419 | // release the device file handle 420 | status |= _i2c_releaseFd(fd); 421 | 422 | return (status); 423 | } 424 | 425 | // write n-bytes to a m-byte to the i2c bus 426 | int i2c_writeMultiByteAddr (int devNum, int devAddr, uint8_t *addrBuffer, int addrLength, uint8_t *buffer, int dataLength) 427 | { 428 | int status, i, size; 429 | uint8_t *bufferNew; 430 | 431 | // allocate the new buffer 432 | size = addrLength + dataLength; // buffer is the size of the address and data bytes combined 433 | bufferNew = malloc( size * sizeof *bufferNew ); 434 | 435 | // add the address to the buffer 436 | for (i = 0; i < addrLength; i++) { 437 | bufferNew[i] = addrBuffer[i]; 438 | } 439 | // copy the data to the buffer (starting at the first index after the last address byte) 440 | memcpy( &bufferNew[i], &buffer[0], (size-1) * sizeof *buffer ); 441 | 442 | // perform the write 443 | status = _i2c_writeBuffer(devNum, devAddr, bufferNew, size); 444 | 445 | // free the allocated memory 446 | free(bufferNew); 447 | 448 | return (status); 449 | } -------------------------------------------------------------------------------- /src/lib/onion-mcp23008-driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | // change the value of a single bit 5 | void _SetBit(int* regVal, int bitNum, int value) 6 | { 7 | (*regVal) ^= (-value ^ (*regVal)) & (1 << bitNum); 8 | } 9 | 10 | // find the value of a single bit 11 | int _GetBit(int regVal, int bitNum) 12 | { 13 | int value; 14 | 15 | // isolate the specific bit 16 | value = ((regVal >> bitNum) & 0x1); 17 | 18 | return (value); 19 | } 20 | 21 | // overwrite all values in register 22 | int _SetReg (int devAddr, int addr, int val) 23 | { 24 | int status; 25 | 26 | status = i2c_write ( MCP23008_I2C_DEVICE_NUM, 27 | MCP23008_I2C_DEVICE_ADDR + devAddr, 28 | addr, 29 | val 30 | ); 31 | if (status == EXIT_FAILURE) { 32 | onionPrint(ONION_SEVERITY_FATAL, "mcp-driver:: writing value 0x%02x to addr 0x%02x failed\n", val, addr); 33 | } 34 | 35 | return status; 36 | } 37 | 38 | // read register value 39 | int _ReadReg (int devAddr, int addr, int* val) 40 | { 41 | int status; 42 | 43 | status = i2c_readByte ( MCP23008_I2C_DEVICE_NUM, 44 | MCP23008_I2C_DEVICE_ADDR + devAddr, 45 | addr, 46 | val 47 | ); 48 | if (status == EXIT_FAILURE) { 49 | onionPrint(ONION_SEVERITY_FATAL, "mcp-driver:: read from addr 0x%02x failed\n", addr); 50 | } 51 | 52 | return status; 53 | } 54 | 55 | // set only a single bit in a register 56 | int _SetRegBit (int devAddr, int addr, int bitNum, int bitVal) 57 | { 58 | int status, val; 59 | 60 | // read the value 61 | status = _ReadReg(devAddr, addr, &val); 62 | if (status == EXIT_FAILURE) { 63 | return status; 64 | } 65 | 66 | // inject the bit value 67 | _SetBit(&val, bitNum, bitVal); 68 | 69 | // write the value 70 | status = _SetReg(devAddr, addr, val); 71 | 72 | return status; 73 | } 74 | 75 | // validate gpio number 76 | int _ValidateGpio (int gpio) 77 | { 78 | int status = EXIT_FAILURE; 79 | 80 | if (gpio >= 0 && gpio < MCP23008_NUM_GPIOS) { 81 | status = EXIT_SUCCESS; 82 | } 83 | 84 | return status; 85 | } 86 | 87 | // function to generalize attribute setting functions 88 | int _SetAttribute(int devAddr, int gpio, int addr, int val) 89 | { 90 | int status; 91 | 92 | // validate the gpio number 93 | status = _ValidateGpio(gpio); 94 | if (status == EXIT_FAILURE) { 95 | return status; 96 | } 97 | 98 | // perform the register write to set the attribute 99 | status = _SetRegBit(devAddr, addr, gpio, val & 0x01); 100 | 101 | return status; 102 | } 103 | 104 | // function to generalize attribute reading functions 105 | int _GetAttribute(int devAddr, int gpio, int addr, int* val) 106 | { 107 | int status, regVal; 108 | 109 | // validate the gpio number 110 | status = _ValidateGpio(gpio); 111 | if (status == EXIT_FAILURE) { 112 | return status; 113 | } 114 | 115 | // read the register 116 | status = _ReadReg(devAddr, addr, ®Val); 117 | if (status == EXIT_FAILURE) { 118 | return status; 119 | } 120 | 121 | // isolate the specific bit 122 | *val = _GetBit(regVal, gpio); 123 | 124 | return status; 125 | } 126 | 127 | 128 | // set attribute functions 129 | int mcp_setDirection (int devAddr, int gpio, int bInput) 130 | { 131 | int status; 132 | 133 | status = _SetAttribute(devAddr, gpio, MCP23008_REG_IODIR, bInput); 134 | 135 | return status; 136 | } 137 | 138 | int mcp_setInputPolarity (int devAddr, int gpio, int bActiveLow) 139 | { 140 | int status; 141 | 142 | status = _SetAttribute(devAddr, gpio, MCP23008_REG_IPOL, bActiveLow); 143 | 144 | return status; 145 | } 146 | 147 | int mcp_setPullup (int devAddr, int gpio, int bPullUp) 148 | { 149 | int status; 150 | 151 | status = _SetAttribute(devAddr, gpio, MCP23008_REG_GPPU, bPullUp); 152 | 153 | return status; 154 | } 155 | 156 | int mcp_setGpio (int devAddr, int gpio, int value) 157 | { 158 | int status; 159 | 160 | status = _SetAttribute(devAddr, gpio, MCP23008_REG_GPIO, value); 161 | 162 | return status; 163 | } 164 | 165 | 166 | // functions to set attribute for all gpios 167 | int mcp_setAllDirection (int devAddr, int bInput) 168 | { 169 | int status; 170 | 171 | status = _SetReg(devAddr, MCP23008_REG_IODIR, bInput); 172 | 173 | return status; 174 | } 175 | 176 | int mcp_setAllInputPolarity (int devAddr, int bActiveLow) 177 | { 178 | int status; 179 | 180 | status = _SetReg(devAddr, MCP23008_REG_IPOL, bActiveLow); 181 | 182 | return status; 183 | } 184 | 185 | int mcp_setAllPullup (int devAddr, int bPullUp) 186 | { 187 | int status; 188 | 189 | status = _SetReg(devAddr, MCP23008_REG_GPPU, bPullUp); 190 | 191 | return status; 192 | } 193 | 194 | int mcp_setAllGpio (int devAddr, int value) 195 | { 196 | int status; 197 | 198 | status = _SetReg(devAddr, MCP23008_REG_GPIO, value); 199 | 200 | return status; 201 | } 202 | 203 | 204 | // functions to read attribute for specific gpio 205 | int mcp_getDirection (int devAddr, int gpio, int* bInput) 206 | { 207 | int status, val; 208 | 209 | status = _GetAttribute(devAddr, gpio, MCP23008_REG_IODIR, bInput); 210 | 211 | return status; 212 | } 213 | 214 | int mcp_getInputPolarity (int devAddr, int gpio, int* bActiveLow) 215 | { 216 | int status, val; 217 | 218 | status = _GetAttribute(devAddr, gpio, MCP23008_REG_IPOL, bActiveLow); 219 | 220 | return status; 221 | } 222 | 223 | int mcp_getPullup (int devAddr, int gpio, int* bPullUp) 224 | { 225 | int status, val; 226 | 227 | status = _GetAttribute(devAddr, gpio, MCP23008_REG_GPPU, bPullUp); 228 | 229 | return status; 230 | } 231 | 232 | int mcp_getGpio (int devAddr, int gpio, int* value) 233 | { 234 | int status, val; 235 | 236 | status = _GetAttribute(devAddr, gpio, MCP23008_REG_GPIO, value); 237 | 238 | return status; 239 | } 240 | 241 | 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /src/main-oled-exp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // function prototypes: 4 | void Usage (const char* progName); 5 | int oledCommand (char *command, char *param); 6 | 7 | // print the usage info 8 | void usage(const char* progName) 9 | { 10 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 11 | onionPrint(ONION_SEVERITY_FATAL, "Usage: oled-exp -i\n"); 12 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 13 | onionPrint(ONION_SEVERITY_FATAL, "FUNCTIONALITY:\n"); 14 | onionPrint(ONION_SEVERITY_FATAL, " Initialize the OLED Display\n"); 15 | onionPrint(ONION_SEVERITY_FATAL, "\n\n"); 16 | 17 | onionPrint(ONION_SEVERITY_FATAL, "Usage: oled-exp -c\n"); 18 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 19 | onionPrint(ONION_SEVERITY_FATAL, "FUNCTIONALITY:\n"); 20 | onionPrint(ONION_SEVERITY_FATAL, " Clear the OLED Display\n"); 21 | onionPrint(ONION_SEVERITY_FATAL, "\n\n"); 22 | 23 | onionPrint(ONION_SEVERITY_FATAL, "Usage: oled-exp [-icqv] COMMAND PARAMETER\n"); 24 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 25 | onionPrint(ONION_SEVERITY_FATAL, "The following COMMANDs are available:\n"); 26 | onionPrint(ONION_SEVERITY_FATAL, " power Turn the display on or off\n"); 27 | onionPrint(ONION_SEVERITY_FATAL, " write Write the input string on the display\n"); 28 | onionPrint(ONION_SEVERITY_FATAL, " writeByte Write the input byte on the display\n"); 29 | onionPrint(ONION_SEVERITY_FATAL, " dim Adjust the screen brightness\n"); 30 | onionPrint(ONION_SEVERITY_FATAL, " invert Invert the colors on the display\n"); 31 | onionPrint(ONION_SEVERITY_FATAL, " cursor , Set the cursor to the specified row and column\n"); 32 | onionPrint(ONION_SEVERITY_FATAL, " cursorPixel , Set the cursor to the specified row and pixel\n"); 33 | onionPrint(ONION_SEVERITY_FATAL, " scroll Enable scrolling of screen content\n"); 34 | onionPrint(ONION_SEVERITY_FATAL, " available directions: left, right, diagonal-left, diagonal-right\n"); 35 | onionPrint(ONION_SEVERITY_FATAL, " to stop scrolling: stop\n"); 36 | onionPrint(ONION_SEVERITY_FATAL, " draw Draw the contents of an lcd file to the display\n"); 37 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 38 | onionPrint(ONION_SEVERITY_FATAL, "COMMANDs can be cascaded one after another, they will execute in order.\n"); 39 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 40 | 41 | onionPrint(ONION_SEVERITY_FATAL, "OPTIONS:\n"); 42 | onionPrint(ONION_SEVERITY_FATAL, " -i initialize display\n"); 43 | onionPrint(ONION_SEVERITY_FATAL, " -c clear the display\n"); 44 | onionPrint(ONION_SEVERITY_FATAL, " -q quiet: no output\n"); 45 | onionPrint(ONION_SEVERITY_FATAL, " -v verbose: lots of output\n"); 46 | onionPrint(ONION_SEVERITY_FATAL, " -h help: show this prompt\n"); 47 | 48 | 49 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 50 | } 51 | 52 | // execute a specified command 53 | int oledCommand(char *command, char *param) 54 | { 55 | int status; 56 | int val0, val1; 57 | uint8_t *buffer; 58 | 59 | // perform the specified command 60 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "command = '%s', param = '%s'\n", command, param); 61 | if (strcmp(command, "write") == 0 ) { 62 | status = oledWrite(param); 63 | } 64 | else if (strcmp(command, "writeByte") == 0 ) { 65 | // parse the byte 66 | if (param[0] == '0' && param[1] == 'x') { 67 | sscanf(param, "0x%02x", &val0); 68 | } 69 | else { 70 | sscanf(param, "%02x", &val0); 71 | } 72 | 73 | status = oledWriteByte(val0); 74 | } 75 | else if (strcmp(command, "brightness") == 0 ) { 76 | status = oledSetBrightness( atoi(param) ); 77 | } 78 | else if (strcmp(command, "invert") == 0 ) { 79 | // interpret the parameter 80 | val0 = 0; // off by default 81 | if (strcmp(param, "on") == 0 ) { 82 | val0 = 1; 83 | } 84 | status = oledSetDisplayMode( val0 ); 85 | } 86 | else if (strcmp(command, "power") == 0 ) { 87 | // interpret the parameter 88 | val0 = 0; // off by default 89 | if (strcmp(param, "on") == 0 ) { 90 | val0 = 1; 91 | } 92 | status = oledSetDisplayPower(val0); 93 | } 94 | else if (strcmp(command, "dim") == 0 ) { 95 | // interpret the parameter 96 | val0 = 0; // off by default 97 | if (strcmp(param, "on") == 0 ) { 98 | val0 = 1; 99 | } 100 | status = oledSetDim(val0); 101 | } 102 | else if (strcmp(command, "cursor") == 0 ) { 103 | // interpret the parameter 104 | sscanf(param, "%d, %d", &val0, &val1); 105 | onionPrint(ONION_SEVERITY_INFO, "> Setting cursor to (%d, %d)\n", val0, val1); 106 | status = oledSetTextColumns(); 107 | status = oledSetCursor(val0, val1); 108 | } 109 | else if (strcmp(command, "cursorPixel") == 0 ) { 110 | // interpret the parameter 111 | sscanf(param, "%d, %d", &val0, &val1); 112 | onionPrint(ONION_SEVERITY_INFO, "> Setting cursor to row: %d, pixel: %d\n", val0, val1); 113 | status = oledSetImageColumns(); 114 | status = oledSetCursorByPixel(val0, val1); 115 | } 116 | else if (strcmp(command, "draw") == 0 ) { 117 | // allocate memory for the buffer 118 | buffer = malloc(OLED_EXP_WIDTH*OLED_EXP_HEIGHT/8 * sizeof *buffer); 119 | 120 | memset(buffer, 0, OLED_EXP_WIDTH*OLED_EXP_HEIGHT/8 * sizeof *buffer); // FIXME: We should definitely do a #define for the buffer size calculation. This looks ugly. 121 | 122 | // read the parameter 123 | if ( strncmp(param, OLED_EXP_READ_LCD_DATA_IDENTIFIER, strlen(OLED_EXP_READ_LCD_DATA_IDENTIFIER) ) == 0 ) { 124 | onionPrint(ONION_SEVERITY_INFO, "> Reading data from argument\n"); 125 | 126 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, " param length is %d\n", strlen(param) ); 127 | // remove the data identifier from the string 128 | memmove ( param, 129 | param + strlen(OLED_EXP_READ_LCD_DATA_IDENTIFIER), 130 | strlen(param) 131 | ); 132 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, " after move: param length is %d\n", strlen(param) ); 133 | 134 | // read the data into a buffer 135 | status = oledReadLcdData(param, buffer); 136 | } 137 | else { 138 | // read data from a file 139 | onionPrint(ONION_SEVERITY_INFO, "> Reading data from file '%s'\n", param); 140 | status = oledReadLcdFile(param, buffer); 141 | } 142 | 143 | if (status == EXIT_SUCCESS) { 144 | status = oledDraw(buffer, OLED_EXP_WIDTH*OLED_EXP_HEIGHT/8); 145 | } 146 | else { 147 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: Cannot draw invalid data\n"); 148 | } 149 | 150 | // deallocate memory for the buffer 151 | if (buffer != NULL) { 152 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "> Deallocating buffer array\n"); 153 | free(buffer); 154 | } 155 | } 156 | else if (strcmp(command, "scroll") == 0 ) { 157 | // interpret the parameters 158 | val0 = -1; 159 | val1 = -1; 160 | if (strcmp(param, "left") == 0) { 161 | val0 = 0; // horizontal scrolling 162 | val1 = 0; // scrolling left 163 | } 164 | else if (strcmp(param, "right") == 0) { 165 | val0 = 0; // horizontal scrolling 166 | val1 = 1; // scrolling right 167 | } 168 | else if (strcmp(param, "diagonal-left") == 0) { 169 | val0 = 1; // vertical scrolling 170 | val1 = 0; // scrolling up 171 | } 172 | else if (strcmp(param, "diagonal-right") == 0) { 173 | val0 = 1; // vertical scrolling 174 | val1 = 1; // scrolling down 175 | } 176 | 177 | if (val0 == -1) { 178 | status = oledScrollStop(); 179 | } 180 | else if (val0 == 0) { 181 | // horizontal scrolling 182 | status = oledScroll(val1, OLED_EXP_SCROLL_SPEED_5_FRAMES, 0, OLED_EXP_CHAR_ROWS-1); 183 | } 184 | else if (val0 == 1) { 185 | // diagonal scrolling 186 | status = oledScrollDiagonal ( val1, // direction 187 | OLED_EXP_SCROLL_SPEED_5_FRAMES, // scroll speed 188 | 0, // # fixed rows 189 | OLED_EXP_HEIGHT, // # scrolling rows 190 | 1, // rows to scroll by 191 | 0, // horizontal start page 192 | OLED_EXP_CHAR_ROWS-1 // horizontal end page 193 | ); 194 | } 195 | } 196 | else { 197 | onionPrint(ONION_SEVERITY_FATAL, "> Unrecognized command '%s'\n", command ); 198 | } 199 | 200 | return status; 201 | } 202 | 203 | int main(int argc, char** argv) 204 | { 205 | const char *progname; 206 | char *command; 207 | char *param; 208 | 209 | int status; 210 | int verbose; 211 | int init; 212 | int clear; 213 | int ch; 214 | 215 | 216 | // set the defaults 217 | init = 0; 218 | clear = 0; 219 | verbose = ONION_VERBOSITY_NORMAL; 220 | 221 | command = malloc(MAX_COMMAND_LENGTH * sizeof *command); 222 | param = malloc(MAX_PARAM_LENGTH * sizeof *param); 223 | 224 | // save the program name 225 | progname = argv[0]; 226 | 227 | 228 | //// parse the option arguments 229 | while ((ch = getopt(argc, argv, "vqhic")) != -1) { 230 | switch (ch) { 231 | case 'v': 232 | // verbose output 233 | verbose++; 234 | break; 235 | case 'q': 236 | // quiet output 237 | verbose = ONION_VERBOSITY_NONE; 238 | break; 239 | case 'i': 240 | // perform init sequence 241 | init = 1; 242 | break; 243 | case 'c': 244 | // perform clear sequence 245 | clear = 1; 246 | break; 247 | default: 248 | usage(progname); 249 | return 0; 250 | } 251 | } 252 | 253 | // set the verbosity 254 | onionSetVerbosity(verbose); 255 | 256 | 257 | // advance past the option arguments 258 | argc -= optind; 259 | argv += optind; 260 | 261 | 262 | //// OLED PROGRAMMING 263 | // check if OLED Expansion is present 264 | status = oledCheckInit(); 265 | 266 | // exit the app if i2c reads fail 267 | if (status == EXIT_FAILURE) { 268 | onionPrint(ONION_SEVERITY_FATAL, "> ERROR: OLED Expansion not found!\n"); 269 | return 0; 270 | } 271 | 272 | 273 | // initialize display 274 | if ( init == 1 ) { 275 | status = oledDriverInit(); 276 | if (status == EXIT_FAILURE) { 277 | onionPrint(ONION_SEVERITY_FATAL, "main-oled-exp:: display init failed!\n"); 278 | } 279 | } 280 | 281 | // clear screen 282 | if ( clear == 1 ) { 283 | onionPrint(ONION_SEVERITY_INFO, "> Clearing display\n"); 284 | status = oledClear(); 285 | if (status == EXIT_FAILURE) { 286 | onionPrint(ONION_SEVERITY_FATAL, "main-oled-exp:: display clear failed!\n"); 287 | } 288 | } 289 | 290 | 291 | // check if just option command 292 | if ( argc == 0 ) { 293 | // check if usage needs to be printed 294 | if ( init == 0 && clear == 0) { 295 | usage(progname); 296 | } 297 | return 0; 298 | } 299 | 300 | 301 | //// parse the command arguments 302 | while ( argc > 0 ) { 303 | if(strlen(argv[0]) > MAX_COMMAND_LENGTH || strlen(argv[1]) > MAX_PARAM_LENGTH) { 304 | // FIXME: This error needs rewording. Also, the exit status should be less funny. 305 | onionPrint(ONION_SEVERITY_FATAL, "Unsupported parameter length\n"); 306 | exit(13); 307 | } 308 | 309 | // first arg - command 310 | strcpy(command, argv[0]); 311 | 312 | // second arg - parameter (optional) 313 | if ( argc > 1 ) { 314 | strcpy(param, argv[1]); 315 | } 316 | 317 | // perform the specified command 318 | status = oledCommand(command, param); 319 | if (status != EXIT_SUCCESS) { 320 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: command '%s' failed!\n", command); 321 | } 322 | 323 | // decrement the number of arguments left 324 | argc -= 2; 325 | argv += 2; 326 | 327 | onionPrint(ONION_SEVERITY_DEBUG, "> arguments remaining: %d\n", argc); 328 | } 329 | 330 | 331 | return 0; 332 | } 333 | -------------------------------------------------------------------------------- /src/main-pwm-exp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef enum e_PwmExpMode { 4 | MAIN_PWM_EXP_DUTY_MODE = 0, 5 | MAIN_PWM_EXP_PERIOD_MODE = 1, 6 | MAIN_PWM_EXP_SLEEP_MODE = 2 7 | } eProgramMode; 8 | 9 | void usage(const char* progName) 10 | { 11 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 12 | onionPrint(ONION_SEVERITY_FATAL, "Usage: pwm-exp -i\n"); 13 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 14 | onionPrint(ONION_SEVERITY_FATAL, "FUNCTIONALITY:\n"); 15 | onionPrint(ONION_SEVERITY_FATAL, "\tOnly initialize the pwm chip\n"); 16 | onionPrint(ONION_SEVERITY_FATAL, "\n\n"); 17 | onionPrint(ONION_SEVERITY_FATAL, "Usage: pwm-exp [-qvi] [-f ] CHANNEL DUTY [DELAY]\n"); 18 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 19 | onionPrint(ONION_SEVERITY_FATAL, "CHANNEL is the specified PWM channel on the Expansion\n"); 20 | onionPrint(ONION_SEVERITY_FATAL, "\tcan be: 0-15 to control a single channel\n"); 21 | onionPrint(ONION_SEVERITY_FATAL, "\tcan be: 'all' to control all channels simultaneously\n"); 22 | onionPrint(ONION_SEVERITY_FATAL, "DUTY is the signal duty cycle, expressed 1-100\n"); 23 | onionPrint(ONION_SEVERITY_FATAL, "DELAY is the delay before signal asserts, optional\n"); 24 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 25 | onionPrint(ONION_SEVERITY_FATAL, "FUNCTIONALITY:\n"); 26 | onionPrint(ONION_SEVERITY_FATAL, "\tProgram the CHANNEL to the specified duty cycle\n"); 27 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 28 | onionPrint(ONION_SEVERITY_FATAL, "OPTIONS:\n"); 29 | onionPrint(ONION_SEVERITY_FATAL, " -q quiet: no output\n"); 30 | onionPrint(ONION_SEVERITY_FATAL, " -v verbose: lots of output\n"); 31 | onionPrint(ONION_SEVERITY_FATAL, " -h help: show this prompt\n"); 32 | onionPrint(ONION_SEVERITY_FATAL, " -i initialize the pwm chip and oscillator\n"); 33 | onionPrint(ONION_SEVERITY_FATAL, " -s put oscillator into sleep mode, disabling all pwm signals\n"); 34 | onionPrint(ONION_SEVERITY_FATAL, " -f set pwm signal frequency\n"); 35 | onionPrint(ONION_SEVERITY_FATAL, "\n\n"); 36 | onionPrint(ONION_SEVERITY_FATAL, "Usage: pwm-exp -p CHANNEL PULSE_WIDTH TOTAL_PERIOD\n"); 37 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 38 | onionPrint(ONION_SEVERITY_FATAL, "CHANNEL is the specified PWM channel on the Expansion\n"); 39 | onionPrint(ONION_SEVERITY_FATAL, "PULSE_WIDTH is the time in ms while the pulse is high\n"); 40 | onionPrint(ONION_SEVERITY_FATAL, "TOTAL_PERIOD is the total period in ms\n"); 41 | 42 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 43 | } 44 | 45 | int readChannelArgument (char* channelArgument) 46 | { 47 | int channel = PWM_EXP_NUM_CHANNELS; // default value is invalid 48 | 49 | if (strcmp(channelArgument, "all") == 0 ) { 50 | channel = -1; // all drivers 51 | } 52 | else { 53 | channel = (int)strtol(channelArgument, NULL, 10); 54 | } 55 | 56 | return channel; 57 | } 58 | 59 | int validateChannelArgument (int channel) 60 | { 61 | int status = EXIT_SUCCESS; 62 | 63 | if (channel < -1 || channel >= PWM_EXP_NUM_CHANNELS) { 64 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid CHANNEL selection\n"); 65 | onionPrint(ONION_SEVERITY_FATAL, "Accepted values are:\n"); 66 | onionPrint(ONION_SEVERITY_FATAL, "\t0-15\n"); 67 | onionPrint(ONION_SEVERITY_FATAL, "\tall\n"); 68 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 69 | 70 | status = EXIT_FAILURE; 71 | } 72 | 73 | return status; 74 | } 75 | 76 | int validateArgumentsDutyMode(int channel, float duty, float delay) 77 | { 78 | int status = EXIT_SUCCESS; 79 | 80 | status = validateChannelArgument(channel); 81 | 82 | if (duty < 0.0f || duty > 100.0f) { 83 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid DUTY selection\n"); 84 | onionPrint(ONION_SEVERITY_FATAL, "Accepted values are:\n"); 85 | onionPrint(ONION_SEVERITY_FATAL, "\t0 - 100\n"); 86 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 87 | 88 | status = EXIT_FAILURE; 89 | } 90 | 91 | if (delay < 0.0f || delay > 100.0f) { 92 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid DELAY selection\n"); 93 | onionPrint(ONION_SEVERITY_FATAL, "Accepted values are:\n"); 94 | onionPrint(ONION_SEVERITY_FATAL, "\t0 - 100\n"); 95 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 96 | 97 | status = EXIT_FAILURE; 98 | } 99 | 100 | return status; 101 | } 102 | 103 | int validateArgumentsPeriodMode(int channel, float periodOn, float periodTotal, float frequency) 104 | { 105 | int status = EXIT_SUCCESS; 106 | 107 | status = validateChannelArgument(channel); 108 | 109 | if (periodOn < 0.0f || periodOn > periodTotal) { 110 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid PERIOD_ON selection\n"); 111 | onionPrint(ONION_SEVERITY_FATAL, "Must fulfill the following conditions:\n"); 112 | onionPrint(ONION_SEVERITY_FATAL, "\tbe greater than 0\n"); 113 | onionPrint(ONION_SEVERITY_FATAL, "\tbe less than or equal to the total period\n"); 114 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 115 | 116 | status = EXIT_FAILURE; 117 | } 118 | 119 | if (periodTotal < 0.0f) { 120 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid PERIOD_TOTAL selection\n"); 121 | onionPrint(ONION_SEVERITY_FATAL, "Must be greater than 0\n"); 122 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 123 | 124 | status = EXIT_FAILURE; 125 | } 126 | 127 | if (frequency < PWM_FREQUENCY_MIN || frequency > PWM_FREQUENCY_MAX) { 128 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid PERIOD_TOTAL selection\n"); 129 | onionPrint(ONION_SEVERITY_FATAL, "Period corresponds to frequency of %0.2f Hz\n", frequency); 130 | onionPrint(ONION_SEVERITY_FATAL, "Period must correspond to frequency between:\n"); 131 | onionPrint(ONION_SEVERITY_FATAL, "\t%d Hz - %d Hz", (int)PWM_FREQUENCY_MIN, (int)PWM_FREQUENCY_MAX); 132 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 133 | 134 | status = EXIT_FAILURE; 135 | } 136 | 137 | return status; 138 | } 139 | 140 | int main(int argc, char** argv) 141 | { 142 | const char *progname; 143 | int status; 144 | int mode; 145 | int verbose; 146 | int init; 147 | int ch; 148 | 149 | int bInitialized; 150 | int channel; 151 | float duty, delay, frequency; 152 | float periodOn, periodTotal; 153 | 154 | // set the defaults 155 | init = 0; 156 | verbose = ONION_VERBOSITY_NORMAL; 157 | mode = MAIN_PWM_EXP_DUTY_MODE; 158 | 159 | frequency = PWM_FREQUENCY_DEFAULT; 160 | delay = 0.0f; // default value 161 | 162 | // save the program name 163 | progname = argv[0]; 164 | 165 | //// parse the option arguments 166 | while ((ch = getopt(argc, argv, "vqhipsf:")) != -1) { 167 | switch (ch) { 168 | case 'v': 169 | // verbose output 170 | verbose++; 171 | break; 172 | case 'q': 173 | // quiet output 174 | verbose = ONION_VERBOSITY_NONE; 175 | break; 176 | case 'i': 177 | // perform init sequence 178 | init = 1; 179 | break; 180 | case 'p': 181 | // enable period mode 182 | mode = MAIN_PWM_EXP_PERIOD_MODE; 183 | break; 184 | case 'f': 185 | // specify the pwm frequency 186 | frequency = atof(optarg); 187 | break; 188 | case 's': 189 | // enable sleep mode 190 | mode = MAIN_PWM_EXP_SLEEP_MODE; 191 | break; 192 | default: 193 | usage(progname); 194 | return 0; 195 | } 196 | } 197 | 198 | // set the verbosity 199 | onionSetVerbosity(verbose); 200 | 201 | 202 | // advance past the option arguments 203 | argc -= optind; 204 | argv += optind; 205 | 206 | 207 | // check if just initialization 208 | if ( argc == 0 && init == 1 ) { 209 | status = pwmDriverInit(); 210 | if (status == EXIT_FAILURE) { 211 | onionPrint(ONION_SEVERITY_FATAL, "main-pwm-exp:: pwm init failed!\n"); 212 | } 213 | return 0; 214 | } 215 | 216 | // check if setting sleep mode 217 | if (mode == MAIN_PWM_EXP_SLEEP_MODE) { 218 | status = pwmDisableChip(); 219 | if (status == EXIT_FAILURE) { 220 | onionPrint(ONION_SEVERITY_FATAL, "main-pwm-exp:: pwm chip disable failed!\n"); 221 | } 222 | return 0; 223 | } 224 | 225 | 226 | 227 | //// parse the real arguments 228 | if (mode == MAIN_PWM_EXP_DUTY_MODE) 229 | { 230 | // ensure correct number of arguments 231 | if ( argc != 2 && argc != 3 ) { 232 | usage(progname); 233 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid amount of arguments!\n"); 234 | return 0; 235 | } 236 | 237 | 238 | //// parse the arguments 239 | // first arg - channel 240 | channel = readChannelArgument(argv[0]); 241 | 242 | // second arg - duty cycle 243 | duty = strtof(argv[1], NULL); 244 | 245 | // third arg, optional - delay value 246 | if (argc == 3) { 247 | delay = strtof(argv[2], NULL); 248 | } 249 | 250 | // validate the arguments 251 | status = validateArgumentsDutyMode(channel, duty, delay); 252 | if (status == EXIT_FAILURE) { 253 | return 0; 254 | } 255 | } 256 | else if (mode == MAIN_PWM_EXP_PERIOD_MODE) 257 | { 258 | // ensure correct number of arguments 259 | if ( argc != 3 ) { 260 | usage(progname); 261 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid amount of arguments!\n"); 262 | return 0; 263 | } 264 | 265 | 266 | //// parse the arguments 267 | // first arg - channel 268 | channel = readChannelArgument(argv[0]); 269 | 270 | // second arg - pulse width 271 | periodOn = strtof(argv[1], NULL); 272 | 273 | // third arg - total period 274 | periodTotal = strtof(argv[2], NULL); 275 | 276 | // convert periods to duty 277 | duty = (periodOn / periodTotal) * 100.0f; 278 | 279 | // convert total period to frequency 280 | frequency = ( 1.0f / (periodTotal / 1000.0f) ); 281 | 282 | // validate the arguments 283 | status = validateArgumentsPeriodMode(channel, periodOn, periodTotal, frequency); 284 | if (status == EXIT_FAILURE) { 285 | return 0; 286 | } 287 | } 288 | 289 | 290 | 291 | //// PWM PROGRAMMING 292 | // check if initialized 293 | status = pwmCheckInit(&bInitialized); 294 | 295 | // exit the app if i2c reads fail 296 | if (status == EXIT_FAILURE) { 297 | onionPrint(ONION_SEVERITY_FATAL, "> ERROR: PWM Expansion not found!\n"); 298 | return 0; 299 | } 300 | 301 | 302 | // perform initialization 303 | if (init == 1 || bInitialized == 0) { 304 | status = pwmDriverInit(); 305 | if (status == EXIT_FAILURE) { 306 | onionPrint(ONION_SEVERITY_FATAL, "main-pwm-exp:: pwm init failed!\n"); 307 | } 308 | } 309 | 310 | // setup the frequency 311 | status = pwmSetFrequency(frequency); 312 | if (status == EXIT_FAILURE) { 313 | onionPrint(ONION_SEVERITY_FATAL, "main-pwm-exp:: pwm set frequency failed!\n"); 314 | } 315 | 316 | // setup the driver 317 | status = pwmSetupDriver(channel, duty, delay); 318 | if (status == EXIT_FAILURE) { 319 | onionPrint(ONION_SEVERITY_FATAL, "main-pwm-exp:: driver setup failed!\n"); 320 | } 321 | 322 | 323 | return 0; 324 | } -------------------------------------------------------------------------------- /src/main-relay-exp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAIN_RELAY_EXP_COMMAND_READ 1 4 | #define MAIN_RELAY_EXP_COMMAND_WRITE 2 5 | 6 | void usage(const char* progName) 7 | { 8 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 9 | onionPrint(ONION_SEVERITY_FATAL, "Usage: relay-exp -i\n"); 10 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 11 | onionPrint(ONION_SEVERITY_FATAL, "FUNCTIONALITY:\n"); 12 | onionPrint(ONION_SEVERITY_FATAL, "\tJust initialize the Relay chip\n"); 13 | onionPrint(ONION_SEVERITY_FATAL, "\n\n"); 14 | 15 | onionPrint(ONION_SEVERITY_FATAL, "Usage: relay-exp [-qvi] [-s ] CHANNEL STATE\n"); 16 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 17 | onionPrint(ONION_SEVERITY_FATAL, "CHANNEL is the specified Relay channel on the Expansion\n"); 18 | onionPrint(ONION_SEVERITY_FATAL, "\tcan be: 0-1 to control a single channel\n"); 19 | onionPrint(ONION_SEVERITY_FATAL, "\tcan be: 'all' to control all channels simultaneously\n"); 20 | onionPrint(ONION_SEVERITY_FATAL, "STATE is the desired operational state of the relay\n"); 21 | onionPrint(ONION_SEVERITY_FATAL, "\tcan be: 0 relay switch is OFF\n"); 22 | onionPrint(ONION_SEVERITY_FATAL, "\tcan be: 1 relay switch is ON\n"); 23 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 24 | onionPrint(ONION_SEVERITY_FATAL, "FUNCTIONALITY:\n"); 25 | onionPrint(ONION_SEVERITY_FATAL, "\tProgram the CHANNEL to the specified relay state\n"); 26 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 27 | 28 | onionPrint(ONION_SEVERITY_FATAL, "Usage: relay-exp [-qvi] [-s ] read CHANNEL\n"); 29 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 30 | onionPrint(ONION_SEVERITY_FATAL, "CHANNEL is the specified Relay channel on the Expansion\n"); 31 | onionPrint(ONION_SEVERITY_FATAL, "\tcan be: 0-1 to control a single channel\n"); 32 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 33 | onionPrint(ONION_SEVERITY_FATAL, "FUNCTIONALITY:\n"); 34 | onionPrint(ONION_SEVERITY_FATAL, "\tRead the state of the relay specified by the CHANNEL\n"); 35 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 36 | 37 | onionPrint(ONION_SEVERITY_FATAL, "OPTIONS:\n"); 38 | onionPrint(ONION_SEVERITY_FATAL, " -q quiet: no output\n"); 39 | onionPrint(ONION_SEVERITY_FATAL, " -v verbose: lots of output\n"); 40 | onionPrint(ONION_SEVERITY_FATAL, " -h help: show this prompt\n"); 41 | onionPrint(ONION_SEVERITY_FATAL, " -i initialize the relay chip\n"); 42 | onionPrint(ONION_SEVERITY_FATAL, " -s dip-switch configuration in binary, not required if 000\n"); 43 | onionPrint(ONION_SEVERITY_FATAL, " -a relay expansion I2C address (mutually exclusive from -s option)\n"); 44 | 45 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 46 | } 47 | 48 | // flip single char, check for valid input 49 | int processSingleSwitch(char* out, char in) 50 | { 51 | int status = EXIT_FAILURE; 52 | 53 | if (in == '0') { 54 | *out = '1'; 55 | status = EXIT_SUCCESS; 56 | } 57 | else if (in == '1') { 58 | *out = '0'; 59 | status = EXIT_SUCCESS; 60 | } 61 | 62 | return status; 63 | } 64 | 65 | // process switch input into addr to be used by MCP chip 66 | // flip the bits (bit0=bit2, bit1=bit1, bit2=bit0) 67 | // invert the bits 68 | // convert to int 69 | int processSwitchAddr(char* addrIn, int* addrOut) 70 | { 71 | char* output; 72 | int status; 73 | 74 | // check the input 75 | if (strlen(addrIn) != 3) { 76 | return EXIT_FAILURE; 77 | } 78 | 79 | // initialize the output 80 | output = (char*)malloc(sizeof(char) * 3); 81 | strcpy(output, RELAY_EXP_ADDR_SWITCH_DEFAULT_VAL); 82 | 83 | // perform the flip 84 | status = processSingleSwitch(&output[0], addrIn[2]); 85 | status |= processSingleSwitch(&output[1], addrIn[1]); 86 | status |= processSingleSwitch(&output[2], addrIn[0]); 87 | 88 | // convert the string to an integer 89 | *addrOut = (int)strtol(output, NULL, 2); 90 | 91 | // free the allocated memory 92 | free(output); 93 | 94 | return status; 95 | } 96 | 97 | int processAddrArgument(char* addrIn, int* addrOut) 98 | { 99 | int status = EXIT_FAILURE; 100 | int addr; 101 | 102 | if (strlen(addrIn) >= 2) { 103 | // parse the address 104 | if (addrIn[0] == '0' && addrIn[1] == 'x') { 105 | sscanf(addrIn, "0x%02x", &addr); 106 | } 107 | else { 108 | sscanf(addrIn, "%02x", &addr); 109 | } 110 | 111 | *addrOut = addr; 112 | status = EXIT_SUCCESS; 113 | } 114 | 115 | return status; 116 | } 117 | 118 | int checkFirstArgument (char* channelArgument, int *action) { 119 | // default action is write 120 | *action = MAIN_RELAY_EXP_COMMAND_WRITE; 121 | 122 | if (strcmp(channelArgument, "read") == 0 ) { 123 | // action is read 124 | *action = MAIN_RELAY_EXP_COMMAND_READ; 125 | } 126 | 127 | return EXIT_SUCCESS; 128 | } 129 | 130 | int readChannelArgument (char* channelArgument) 131 | { 132 | int channel = RELAY_EXP_NUM_CHANNELS; // default value is invalid 133 | 134 | if (strcmp(channelArgument, "all") == 0 ) { 135 | channel = -1; // all drivers 136 | } 137 | else { 138 | channel = (int)strtol(channelArgument, NULL, 10); 139 | } 140 | 141 | return channel; 142 | } 143 | 144 | int validateArguments(int channel, int state, int bExtended) 145 | { 146 | int status = EXIT_SUCCESS; 147 | int maxNumChannels = RELAY_EXP_NUM_CHANNELS_DEFAULT; 148 | 149 | if (bExtended == 1) { 150 | maxNumChannels = RELAY_EXP_NUM_CHANNELS_EXTENDED; 151 | } 152 | 153 | if (channel < -1 || channel >= maxNumChannels) { 154 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid CHANNEL selection\n"); 155 | onionPrint(ONION_SEVERITY_FATAL, "Accepted values are:\n"); 156 | onionPrint(ONION_SEVERITY_FATAL, "\t0-%d\n", maxNumChannels-1); 157 | onionPrint(ONION_SEVERITY_FATAL, "\tall\n"); 158 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 159 | 160 | status = EXIT_FAILURE; 161 | } 162 | 163 | if (state != 0 && state != 1) { 164 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid STATE selection\n"); 165 | onionPrint(ONION_SEVERITY_FATAL, "Accepted values are:\n"); 166 | onionPrint(ONION_SEVERITY_FATAL, "\t0 or 1\n"); 167 | onionPrint(ONION_SEVERITY_FATAL, "\n"); 168 | 169 | status = EXIT_FAILURE; 170 | } 171 | 172 | return status; 173 | } 174 | 175 | 176 | int main(int argc, char** argv) 177 | { 178 | const char *progname; 179 | char *switchAddr; 180 | char *deviceAddr = NULL; 181 | 182 | int status; 183 | int verbose; 184 | int init; 185 | int ch; 186 | 187 | int programAction; 188 | int channel; 189 | int relayState; 190 | 191 | int devAddr; 192 | int bInitialized; 193 | int bExtended; 194 | 195 | // set defaults 196 | init = 0; 197 | verbose = ONION_VERBOSITY_NORMAL; 198 | bExtended = 0; 199 | 200 | switchAddr = malloc(RELAY_EXP_ADDR_SWITCH_NUM * sizeof *switchAddr); 201 | strcpy(switchAddr, RELAY_EXP_ADDR_SWITCH_DEFAULT_VAL); 202 | 203 | // save the program name 204 | progname = argv[0]; 205 | 206 | //// parse the option arguments 207 | while ((ch = getopt(argc, argv, "vqhies:a:")) != -1) { 208 | switch (ch) { 209 | case 'v': 210 | // verbose output 211 | verbose++; 212 | break; 213 | case 'q': 214 | // quiet output 215 | verbose = ONION_VERBOSITY_NONE; 216 | break; 217 | case 'i': 218 | // perform init sequence 219 | init = 1; 220 | break; 221 | case 'e': 222 | // enable the extended channels 223 | bExtended = 1; 224 | break; 225 | case 's': 226 | // capture binary 227 | strcpy (switchAddr, optarg); 228 | break; 229 | case 'a': 230 | // capture the address 231 | deviceAddr = malloc(strlen(optarg) * sizeof *deviceAddr); 232 | strncpy (deviceAddr, optarg, strlen(optarg) ); 233 | break; 234 | default: 235 | usage(progname); 236 | return 0; 237 | } 238 | } 239 | 240 | // set the verbosity 241 | onionSetVerbosity(verbose); 242 | 243 | 244 | // advance past the option arguments 245 | argc -= optind; 246 | argv += optind; 247 | 248 | 249 | // process the switch address 250 | status = processSwitchAddr(switchAddr, &devAddr); 251 | if (status == EXIT_FAILURE) { 252 | usage(progname); 253 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid switch address argument!\n"); 254 | return 0; 255 | } 256 | if (strcmp(switchAddr, RELAY_EXP_ADDR_SWITCH_DEFAULT_VAL) != 0) { 257 | onionPrint(ONION_SEVERITY_INFO, "> Switch configuration: %s\n", switchAddr); 258 | } 259 | 260 | // process the address argument (overrides the switch address argument) 261 | if (deviceAddr != NULL) { 262 | status = processAddrArgument(deviceAddr, &devAddr); 263 | 264 | if (devAddr != RELAY_EXP_ADDR_DEFAULT) { 265 | onionPrint(ONION_SEVERITY_INFO, "> Using device address: 0x%02x\n", devAddr); 266 | } 267 | // the mcp23008 function expect an offset from the default 0x20 address 268 | devAddr -= MCP23008_I2C_DEVICE_ADDR; 269 | 270 | // clean-up 271 | free(deviceAddr); 272 | } 273 | 274 | 275 | // check if just initialization 276 | if ( argc == 0 && init == 1 ) { 277 | status = relayDriverInit(devAddr); 278 | if (status == EXIT_FAILURE) { 279 | onionPrint(ONION_SEVERITY_FATAL, "main-relay-exp:: relay init failed!\n"); 280 | } 281 | return 0; 282 | } 283 | 284 | // ensure correct number of arguments 285 | if ( argc != 2) { 286 | usage(progname); 287 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: invalid amount of arguments!\n"); 288 | return 0; 289 | } 290 | 291 | 292 | //// parse the arguments 293 | // find if reading or writing state 294 | checkFirstArgument(argv[0], &programAction); 295 | 296 | if (programAction == MAIN_RELAY_EXP_COMMAND_WRITE) { 297 | // first arg - channel 298 | channel = readChannelArgument(argv[0]); 299 | 300 | // second arg - relay state (on or off) 301 | if ( strcmp(argv[1], "off") == 0 || 302 | strcmp(argv[1], "Off") == 0 || 303 | strcmp(argv[1], "OFF") == 0 304 | ) 305 | { 306 | relayState = 0; 307 | } 308 | else if ( strcmp(argv[1], "on") == 0 || 309 | strcmp(argv[1], "On") == 0 || 310 | strcmp(argv[1], "ON") == 0 311 | ) 312 | { 313 | relayState = 1; 314 | } 315 | else { 316 | relayState = (int)strtol(argv[1], NULL, 10); 317 | } 318 | 319 | // validate the arguments 320 | status = validateArguments(channel, relayState, bExtended); 321 | if (status == EXIT_FAILURE) { 322 | return 0; 323 | } 324 | } 325 | else if (programAction == MAIN_RELAY_EXP_COMMAND_READ) { 326 | // first arg - channel 327 | channel = readChannelArgument(argv[1]); 328 | } 329 | 330 | 331 | //// RELAY PROGRAMMING 332 | // check if initialized 333 | status = relayCheckInit(devAddr, &bInitialized); 334 | 335 | // exit the app if i2c reads fail 336 | if (status == EXIT_FAILURE) { 337 | onionPrint(ONION_SEVERITY_FATAL, "> ERROR: Relay Expansion not found!\n"); 338 | return 0; 339 | } 340 | 341 | 342 | // perform initialization 343 | if (init == 1 || bInitialized == 0) { 344 | status = relayDriverInit(devAddr); 345 | if (status == EXIT_FAILURE) { 346 | onionPrint(ONION_SEVERITY_FATAL, "main-relay-exp:: relay init failed!\n"); 347 | } 348 | } 349 | 350 | // set the relay state 351 | if (programAction == MAIN_RELAY_EXP_COMMAND_WRITE) { 352 | if (channel < 0) { 353 | // program both relays at once 354 | status = relaySetAllChannels(devAddr, relayState); 355 | if (status == EXIT_FAILURE) { 356 | onionPrint(ONION_SEVERITY_FATAL, "main-relay-exp:: all relay setup failed!\n"); 357 | } 358 | } 359 | else { 360 | // program just one relay 361 | status = relaySetChannel(devAddr, channel, relayState); 362 | if (status == EXIT_FAILURE) { 363 | onionPrint(ONION_SEVERITY_FATAL, "main-relay-exp:: relay %d setup failed!\n", channel); 364 | } 365 | } 366 | } 367 | else if (programAction == MAIN_RELAY_EXP_COMMAND_READ) { 368 | status = relayReadChannel(devAddr, channel, &relayState); 369 | if (status == EXIT_FAILURE) { 370 | onionPrint(ONION_SEVERITY_FATAL, "main-relay-exp:: reading relay %d state failed!\n", channel); 371 | } 372 | } 373 | 374 | 375 | // clean-up 376 | free(switchAddr); 377 | 378 | 379 | return 0; 380 | } 381 | 382 | -------------------------------------------------------------------------------- /src/oled-exp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Send command byte to OLED Expansion 4 | int _oledSendCommand (int command) 5 | { 6 | int status; 7 | 8 | status = i2c_write(OLED_EXP_DEVICE_NUM, OLED_EXP_ADDR, OLED_EXP_REG_COMMAND, (uint8_t)command); 9 | 10 | return status; 11 | } 12 | 13 | // Send data byte to OLED Expansion 14 | int _oledSendData (int data) 15 | { 16 | int status; 17 | 18 | status = i2c_write(OLED_EXP_DEVICE_NUM, OLED_EXP_ADDR, OLED_EXP_REG_DATA, (uint8_t)data); 19 | 20 | return status; 21 | } 22 | 23 | // Initialize the OLED Expansion 24 | int oledDriverInit () 25 | { 26 | int status; 27 | 28 | onionPrint(ONION_SEVERITY_INFO, "> Initializing display\n"); 29 | 30 | memset(_buffer, 0, sizeof(_buffer)); // Initialize the buffer 31 | _cursor = 0; // Initialize the cursor 32 | _cursorInRow = 0; // Initialize the row cursor 33 | 34 | // set defaults 35 | _vccState = OLED_EXP_SWITCH_CAP_VCC; 36 | 37 | // Initialize the screen for 128x64 38 | status = _oledSendCommand(OLED_EXP_DISPLAY_OFF); 39 | usleep(4500); 40 | 41 | status |= _oledSendCommand(OLED_EXP_SET_DISPLAY_CLOCK_DIV); 42 | status |= _oledSendCommand(0x80); // The suggested ratio 0x80 43 | status |= _oledSendCommand(OLED_EXP_SET_MULTIPLEX); 44 | status |= _oledSendCommand(0x3F); 45 | status |= _oledSendCommand(OLED_EXP_SET_DISPLAY_OFFSET); 46 | status |= _oledSendCommand(0x00); // no offset 47 | status |= _oledSendCommand(OLED_EXP_SET_START_LINE | 0x00); // Set start line to line #0 48 | status |= _oledSendCommand(OLED_EXP_CHARGE_PUMP); 49 | if (_vccState == OLED_EXP_EXTERNAL_VCC) { 50 | status |= _oledSendCommand(0x10); 51 | } else { 52 | status |= _oledSendCommand(0x14); 53 | } 54 | 55 | status |= _oledSendCommand(OLED_EXP_MEMORY_MODE); 56 | status |= _oledSendCommand(OLED_EXP_MEM_HORIZONTAL_ADDR_MODE); 57 | status |= _oledSendCommand(OLED_EXP_SEG_REMAP | 0x01); 58 | status |= _oledSendCommand(OLED_EXP_COM_SCAN_DEC); 59 | status |= _oledSendCommand(OLED_EXP_SET_COM_PINS); 60 | status |= _oledSendCommand(0x12); 61 | status |= _oledSendCommand(OLED_EXP_SET_CONTRAST); 62 | if (_vccState == OLED_EXP_EXTERNAL_VCC) { 63 | status |= _oledSendCommand(OLED_EXP_DEF_CONTRAST_EXTERNAL_VCC); 64 | } else { 65 | status |= _oledSendCommand(OLED_EXP_DEF_CONTRAST_SWITCH_CAP_VCC); 66 | } 67 | 68 | status |= _oledSendCommand(OLED_EXP_SET_PRECHARGE); 69 | if (_vccState == OLED_EXP_EXTERNAL_VCC) { 70 | status |= _oledSendCommand(0x22); 71 | } else { 72 | status |= _oledSendCommand(0xF1); 73 | } 74 | status |= _oledSendCommand(OLED_EXP_SET_VCOM_DETECT); 75 | status |= _oledSendCommand(0x40); 76 | status |= _oledSendCommand(OLED_EXP_DISPLAY_ALL_ON_RESUME); 77 | status |= _oledSendCommand(OLED_EXP_NORMAL_DISPLAY); 78 | 79 | status |= _oledSendCommand(OLED_EXP_SEG_REMAP); 80 | status |= _oledSendCommand(OLED_EXP_COM_SCAN_INC); 81 | 82 | status |= _oledSendCommand(OLED_EXP_DISPLAY_ON); 83 | usleep(4500); 84 | 85 | // clear the display 86 | status |= oledSetDisplayMode(0); // set normal display 87 | status |= oledClear(); 88 | 89 | 90 | return EXIT_SUCCESS; 91 | } 92 | 93 | // Check if i2c transmissions to the display work 94 | int oledCheckInit() 95 | { 96 | int status, data; 97 | 98 | status = i2c_readByte(OLED_EXP_DEVICE_NUM, OLED_EXP_ADDR, OLED_EXP_REG_COMMAND, &data); 99 | 100 | return status; 101 | } 102 | 103 | // Clear the OLED screen 104 | int oledClear() 105 | { 106 | int status; 107 | int charRow, pixelCol; 108 | 109 | onionPrint(ONION_SEVERITY_DEBUG, "> Clearing display\n"); 110 | 111 | // set the column addressing for the full width 112 | status = oledSetImageColumns(); 113 | 114 | // display off 115 | status = _oledSendCommand(OLED_EXP_DISPLAY_OFF); 116 | 117 | // write a blank space to each character 118 | for (charRow = 0; charRow < OLED_EXP_CHAR_ROWS; charRow++) { 119 | oledSetCursor(charRow, 0); 120 | 121 | for (pixelCol = 0; pixelCol < OLED_EXP_WIDTH; pixelCol++) { 122 | status = _oledSendData(0x00); 123 | } 124 | } 125 | 126 | // display on 127 | status |= _oledSendCommand(OLED_EXP_DISPLAY_ON); 128 | // reset the cursor to (0, 0) 129 | status |= oledSetCursor(0, 0); 130 | 131 | return status; 132 | } 133 | 134 | // set the display to on or off 135 | int oledSetDisplayPower(int bPowerOn) 136 | { 137 | int status; 138 | uint8_t cmd; 139 | 140 | onionPrint(ONION_SEVERITY_INFO, "> Setting display to %s\n", (bPowerOn == 1 ? "ON" : "OFF") ); 141 | 142 | // set the command code 143 | if (bPowerOn == 1) { 144 | cmd = OLED_EXP_DISPLAY_ON; 145 | } 146 | else if (bPowerOn == 0) { 147 | cmd = OLED_EXP_DISPLAY_OFF; 148 | } 149 | 150 | // send the command code 151 | status = _oledSendCommand(cmd); 152 | 153 | return status; 154 | } 155 | 156 | // set the display to normal or inverted mode 157 | int oledSetDisplayMode(int bInvert) 158 | { 159 | int status; 160 | uint8_t cmd; 161 | 162 | onionPrint( (bInvert == 1 ? ONION_SEVERITY_INFO : ONION_SEVERITY_DEBUG), 163 | "> Setting display mode to %s\n", 164 | (bInvert == 1 ? "inverted" : "normal") 165 | ); 166 | 167 | // set the command code 168 | if (bInvert == 0) { 169 | cmd = OLED_EXP_NORMAL_DISPLAY; 170 | } 171 | else if (bInvert == 1) { 172 | cmd = OLED_EXP_INVERT_DISPLAY; 173 | } 174 | 175 | // send the command code 176 | status = _oledSendCommand(cmd); 177 | 178 | return status; 179 | } 180 | 181 | // set the display's brightness 182 | int oledSetBrightness(int brightness) 183 | { 184 | int status; 185 | int brightnessAdj; 186 | 187 | // clamp the brightness to the lower and upper limits 188 | brightnessAdj = brightness; 189 | if (brightness < OLED_EXP_CONTRAST_MIN) { 190 | brightnessAdj = OLED_EXP_CONTRAST_MIN; 191 | } 192 | if (brightness > OLED_EXP_CONTRAST_MAX) { 193 | brightnessAdj = OLED_EXP_CONTRAST_MAX; 194 | } 195 | 196 | // send the command 197 | onionPrint(ONION_SEVERITY_DEBUG, "> Setting display brightness to %d/%d\n", brightnessAdj, OLED_EXP_CONTRAST_MAX); 198 | status = _oledSendCommand(OLED_EXP_SET_CONTRAST); 199 | status |= _oledSendCommand(brightnessAdj); 200 | 201 | return status; 202 | } 203 | 204 | // set the screen to normal brightness or dimmed brightness 205 | int oledSetDim(int bDim) 206 | { 207 | int status; 208 | int brightness; 209 | 210 | // set the brightness based on the dimness setting 211 | if (bDim == 1) { 212 | // dim 213 | brightness = OLED_EXP_CONTRAST_MIN; 214 | onionPrint(ONION_SEVERITY_INFO, "> Dimming display\n"); 215 | } 216 | else if (bDim == 0) { 217 | // normal 218 | brightness = OLED_EXP_DEF_CONTRAST_SWITCH_CAP_VCC; 219 | if (_vccState == OLED_EXP_EXTERNAL_VCC) { 220 | brightness = OLED_EXP_DEF_CONTRAST_EXTERNAL_VCC; 221 | } 222 | onionPrint(ONION_SEVERITY_INFO, "> Setting normal display brightness\n"); 223 | } 224 | 225 | // send the command 226 | status = oledSetBrightness(brightness); 227 | 228 | return status; 229 | } 230 | 231 | // set the display's memory mode 232 | int oledSetMemoryMode(int mode) 233 | { 234 | int status; 235 | 236 | // check the input 237 | if ( mode != OLED_EXP_MEM_HORIZONTAL_ADDR_MODE && 238 | mode != OLED_EXP_MEM_VERTICAL_ADDR_MODE && 239 | mode != OLED_EXP_MEM_PAGE_ADDR_MODE 240 | ) 241 | { 242 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: Attempting to set invalid memory mode (0x%02x)\n", mode); 243 | return EXIT_FAILURE; 244 | } 245 | 246 | // send the command 247 | status = _oledSendCommand(OLED_EXP_MEMORY_MODE); 248 | status = _oledSendCommand(mode); 249 | 250 | // store the memory mode 251 | _memoryMode = mode; 252 | 253 | return status; 254 | } 255 | 256 | 257 | // set the OLED's cursor (according to character rows and columns) 258 | int oledSetCursor(int row, int column) 259 | { 260 | int status; 261 | 262 | onionPrint(ONION_SEVERITY_DEBUG, "> Setting cursor to (%d, %d)\n", row, column); 263 | 264 | // check the inputs 265 | if (row < 0 || row >= OLED_EXP_CHAR_ROWS) { 266 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: Attempting to set cursor to invalid row '%d'\n", row); 267 | return EXIT_FAILURE; 268 | } 269 | if (column < 0 || column >= OLED_EXP_CHAR_COLUMNS) { 270 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: Attempting to set cursor to invalid column '%d'\n", column); 271 | return EXIT_FAILURE; 272 | } 273 | 274 | //// set the cursor 275 | // set page address 276 | status |= _oledSendCommand(OLED_EXP_ADDR_BASE_PAGE_START + row); 277 | 278 | // set column lower address 279 | status |= _oledSendCommand(OLED_EXP_SET_LOW_COLUMN + (OLED_EXP_CHAR_LENGTH * column & 0x0F) ); 280 | 281 | // set column higher address 282 | status |= _oledSendCommand(OLED_EXP_SET_HIGH_COLUMN + ((OLED_EXP_CHAR_LENGTH * column >> 4) & 0x0F) ); 283 | 284 | return status; 285 | } 286 | 287 | // set the OLED's cursor (according to character rows and dislay pixels) 288 | int oledSetCursorByPixel(int row, int pixel) 289 | { 290 | int status; 291 | 292 | onionPrint(ONION_SEVERITY_DEBUG, "> Setting cursor to row %d, pixel %d)\n", row, pixel); 293 | 294 | // check the inputs 295 | if (row < 0 || row >= OLED_EXP_CHAR_ROWS) { 296 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: Attempting to set cursor to invalid row '%d'\n", row); 297 | return EXIT_FAILURE; 298 | } 299 | if (pixel < 0 || pixel >= OLED_EXP_WIDTH) { 300 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: Attempting to set cursor to invalid pixel '%d'\n", pixel); 301 | return EXIT_FAILURE; 302 | } 303 | 304 | //// set the cursor 305 | // set page address 306 | status |= _oledSendCommand(OLED_EXP_ADDR_BASE_PAGE_START + row); 307 | 308 | // set pixel lower address 309 | status |= _oledSendCommand(OLED_EXP_SET_LOW_COLUMN + (pixel & 0x0F) ); 310 | 311 | // set pixel higher address 312 | status |= _oledSendCommand(OLED_EXP_SET_HIGH_COLUMN + ((pixel >> 4) & 0x0F) ); 313 | 314 | return status; 315 | } 316 | 317 | 318 | 319 | // set the horizontal addressing 320 | int oledSetColumnAddressing (int startPixel, int endPixel) 321 | { 322 | int status; 323 | 324 | // check the inputs 325 | if (startPixel < 0 || startPixel >= OLED_EXP_WIDTH || startPixel >= endPixel) { 326 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: Invalid start pixel (%d) for column address setup\n", startPixel); 327 | return EXIT_FAILURE; 328 | } 329 | if (endPixel < 0 || endPixel >= OLED_EXP_WIDTH) { 330 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: Invalid end pixel (%d) for column address setup\n", endPixel); 331 | return EXIT_FAILURE; 332 | } 333 | 334 | // set column addressing 335 | status = _oledSendCommand(OLED_EXP_COLUMN_ADDR); 336 | status |= _oledSendCommand(startPixel); // start pixel setup 337 | status |= _oledSendCommand(endPixel); // end pixel setup 338 | 339 | return status; 340 | } 341 | 342 | // set the horizontal addressing for text (fit 6-pixel wide characters onto 128 pixel line) 343 | int oledSetTextColumns () 344 | { 345 | int status; 346 | 347 | // set the column addressing for text mode 348 | status = oledSetColumnAddressing(0, OLED_EXP_CHAR_COLUMN_PIXELS-1); 349 | _bColumnsSetForText = 1; 350 | 351 | return status; 352 | } 353 | 354 | // set the horizontal addressing for images (rows go from pixel 0 to 127, full width) 355 | int oledSetImageColumns () 356 | { 357 | int status; 358 | 359 | // set the column addressing to full width 360 | status = oledSetColumnAddressing(0, OLED_EXP_WIDTH-1); 361 | _bColumnsSetForText = 0; 362 | 363 | return status; 364 | } 365 | 366 | 367 | //// writing functions //// 368 | // Write a character directly to the OLED display (at the OLED cursor's current position) 369 | int oledWriteChar(char c) 370 | { 371 | int status; 372 | int idx; 373 | int charIndex = (int) c - 32; 374 | 375 | // ensure character is in the table 376 | if (charIndex >= 0 && charIndex < sizeof(asciiTable) / sizeof(asciiTable[0])) { 377 | 378 | // write the data for the character 379 | for (idx = 0; idx < OLED_EXP_CHAR_LENGTH; idx++) { 380 | status = _oledSendData(asciiTable[charIndex][idx]); 381 | } 382 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "\twriting '%c' to column %d\n", c, _cursorInRow); 383 | 384 | // increment row cursor 385 | if (_cursorInRow == OLED_EXP_CHAR_COLUMNS - 1) { 386 | _cursorInRow = 0; 387 | } 388 | else { 389 | _cursorInRow++; 390 | } 391 | } 392 | 393 | return status; 394 | } 395 | 396 | // Write a string message directly to the display 397 | int oledWrite (char *msg) 398 | { 399 | int status; 400 | int idx, i; 401 | 402 | onionPrint(ONION_SEVERITY_INFO, "> Writing '%s' to display\n", msg); 403 | 404 | // set addressing mode to page 405 | //oledSetMemoryMode(OLED_EXP_MEM_PAGE_ADDR_MODE); // want automatic newlines enabled 406 | 407 | // set column addressing to fit 126 characters that are 6 pixels wide 408 | if (_bColumnsSetForText == 0) { 409 | status = oledSetTextColumns(); 410 | } 411 | 412 | // write each character 413 | for (idx = 0; idx < strlen(msg); idx++) { 414 | // check for newline character 415 | if (msg[idx] == '\\' && msg[idx+1] == 'n' && msg[idx-1] != '\\' ) { 416 | // move the cursor to the next row 417 | for(i = _cursorInRow; i < OLED_EXP_CHAR_COLUMNS; i++) { 418 | status = oledWriteChar(' '); 419 | } 420 | 421 | // increment past this newline character (skip next index) 422 | idx++; 423 | } 424 | else { 425 | status = oledWriteChar(msg[idx]); 426 | } 427 | } 428 | 429 | // reset the column addressing 430 | status = oledSetImageColumns(); 431 | 432 | return status; 433 | } 434 | 435 | // Write a byte directly to the OLED display (at the OLED cursor's current position) 436 | int oledWriteByte(int byte) 437 | { 438 | int status; 439 | 440 | // write the single byte 441 | status = _oledSendData(byte); 442 | 443 | return status; 444 | } 445 | 446 | // function to reverse the bits in a byte 447 | uint8_t _reverseByte (uint8_t input) { 448 | uint8_t i; 449 | uint8_t rev = 0;; 450 | 451 | for (i = 0; i < 8; i++) { 452 | rev |= ((input >> i) & 0x01) << (7 - i); 453 | } 454 | 455 | return rev; 456 | } 457 | 458 | // Write a buffer directly to the display 459 | int oledDraw (uint8_t *buffer, int bytes) 460 | { 461 | int status; 462 | int idx; 463 | //int swap; // removing bit flip 464 | 465 | onionPrint(ONION_SEVERITY_INFO, "> Writing buffer data to display\n"); 466 | 467 | // set the column addressing for the full width 468 | status = oledSetImageColumns(); 469 | 470 | // set addressing mode to horizontal (automatic newline at the end of each line) 471 | oledSetMemoryMode(OLED_EXP_MEM_HORIZONTAL_ADDR_MODE); 472 | 473 | // write each byte 474 | for (idx = 0; idx < bytes; idx++) { 475 | //swap = _reverseByte(buffer[idx]); 476 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, ">> writing byte %d 0x%02x\n", idx, buffer[idx] ); 477 | //status = _oledSendData(swap); 478 | status = _oledSendData(buffer[idx]); 479 | 480 | } 481 | 482 | return status; 483 | } 484 | 485 | 486 | //// scrolling functions //// 487 | // horizontal scrolling 488 | // direction scrolling 489 | // 0 left 490 | // 1 right 491 | int oledScroll (int direction, int scrollSpeed, int startPage, int stopPage) 492 | { 493 | int status; 494 | int scrollMode; 495 | 496 | onionPrint(ONION_SEVERITY_INFO, "> Enabling horizontal scrolling to the %s\n", (direction == 1 ? "right" : "left") ); 497 | 498 | // read the direction 499 | if (direction == 1) { 500 | scrollMode = OLED_EXP_RIGHT_HORIZONTAL_SCROLL; 501 | } 502 | else if (direction == 0) { 503 | scrollMode = OLED_EXP_LEFT_HORIZONTAL_SCROLL; 504 | } 505 | 506 | // send the commands 507 | status = _oledSendCommand(scrollMode); 508 | status |= _oledSendCommand(0x00); // dummy byte 509 | status |= _oledSendCommand(startPage); // start page addr (0 - 7) 510 | status |= _oledSendCommand(scrollSpeed); // time interval between frames 511 | status |= _oledSendCommand(stopPage); // end page addr (must be greater than start) 512 | status |= _oledSendCommand(0x00); // dummy byte (must be 0x00) 513 | status |= _oledSendCommand(0xff); // dummy byte (must be 0xff) 514 | 515 | status |= _oledSendCommand(OLED_EXP_ACTIVATE_SCROLL); 516 | 517 | return status; 518 | } 519 | 520 | // diagonal scrolling 521 | // direction scrolling 522 | // 0 left 523 | // 1 right 524 | int oledScrollDiagonal (int direction, int scrollSpeed, int fixedRows, int scrollRows, int verticalOffset, int startPage, int stopPage) 525 | { 526 | int status; 527 | int scrollMode; 528 | 529 | onionPrint(ONION_SEVERITY_INFO, "> Enabling diagonal scrolling to the %s\n", (direction == 1 ? "right" : "left") ); 530 | 531 | // read the direction 532 | if (direction == 1) { 533 | scrollMode = OLED_EXP_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL; 534 | } 535 | else if (direction == 0) { 536 | scrollMode = OLED_EXP_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL; 537 | } 538 | 539 | //// send the commands 540 | // setup the vertical scrolling 541 | status = _oledSendCommand(OLED_EXP_SET_VERTICAL_SCROLL_AREA); 542 | status |= _oledSendCommand(fixedRows); // number of fixed rows 543 | status |= _oledSendCommand(scrollRows); // number of rows in scroll area 544 | 545 | // setup the horizontal scrolling 546 | status |= _oledSendCommand(scrollMode); 547 | status |= _oledSendCommand(0x00); // dummy byte 548 | status |= _oledSendCommand(startPage); // start page addr (0 - 7) 549 | status |= _oledSendCommand(scrollSpeed); // time interval between frames 550 | status |= _oledSendCommand(stopPage); // end page addr (must be greater than start) 551 | status |= _oledSendCommand(verticalOffset);// number of rows to scroll by 552 | 553 | status |= _oledSendCommand(OLED_EXP_ACTIVATE_SCROLL); 554 | 555 | return status; 556 | } 557 | 558 | // disable scrolling 559 | int oledScrollStop () 560 | { 561 | int status; 562 | 563 | onionPrint(ONION_SEVERITY_INFO, "> Disabling scrolling\n"); 564 | 565 | // send the command 566 | status |= _oledSendCommand(OLED_EXP_DEACTIVATE_SCROLL); 567 | 568 | return status; 569 | } 570 | 571 | 572 | //// reading lcd data //// 573 | // read a file with hex data 574 | int oledReadLcdFile(char* file, uint8_t *buffer) 575 | { 576 | int idx; 577 | FILE *fp; 578 | unsigned int val; 579 | 580 | // open the file 581 | fp = fopen(file, "r"); 582 | if (fp == NULL) { 583 | onionPrint(ONION_SEVERITY_FATAL, "ERROR: cannot open file '%s'\n", file); 584 | return EXIT_FAILURE; 585 | } 586 | 587 | // read each byte, add to the buffer 588 | idx = 0; 589 | while ( fscanf(fp, OLED_EXP_READ_LCD_STRING_OPT1, &val) > 0 ) { 590 | buffer[idx] = (uint8_t)val; 591 | idx++; 592 | } 593 | 594 | // close the file 595 | fclose(fp); 596 | 597 | return EXIT_SUCCESS; 598 | } 599 | 600 | // read hex data from a string 601 | int oledReadLcdData(char* data, uint8_t *buffer) 602 | { 603 | int idx, i; 604 | unsigned int val; 605 | 606 | //DBG 607 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "\n\n\ndata is of length %d, data:\n%s\n",strlen(data), data); 608 | 609 | // read each byte, add to the buffer 610 | idx = 0; 611 | while ( sscanf(data, OLED_EXP_READ_LCD_STRING_OPT1, &val) > 0 ) { 612 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "idx: %d, val: 0x%02x\n", idx, val); 613 | buffer[idx] = (uint8_t)val; 614 | 615 | // advance the buffer index 616 | idx++; 617 | // advance the string by 2 characters 618 | memmove(data, data+2, strlen(data) ); 619 | } 620 | 621 | //DBG 622 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "buffer: idx is %d, buffer: \n", idx); 623 | for (i = 0; i < idx; i++) { 624 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "idx: %d, 0x%02x\n", i, buffer[i]); 625 | } 626 | onionPrint(ONION_SEVERITY_DEBUG_EXTRA, "\n"); 627 | 628 | return EXIT_SUCCESS; 629 | } 630 | 631 | 632 | //// buffer functions //// 633 | // Write display buffer to OLED 634 | int oledDisplay () 635 | { 636 | _oledSendCommand(OLED_EXP_COLUMN_ADDR); 637 | _oledSendCommand(0x00); // Column start address (0x00 = reset) 638 | _oledSendCommand(OLED_EXP_WIDTH - 1); // Column end address 639 | _oledSendCommand(OLED_EXP_PAGE_ADDR); 640 | _oledSendCommand(0x00); // Page start address (0x00 = reset) 641 | _oledSendCommand(OLED_EXP_PAGES - 1); // Page end address 642 | 643 | // to do: can write entire buffer at once 644 | /* 645 | for (int i = 0; i < sizeof(_buffer) / sizeof(_buffer[0]); i += I2C_BUFFER_SIZE) { 646 | int controlRegister = 0x40; 647 | i2c_writeBuffer(OLED_EXP_DEVICE_NUM, OLED_EXP_ADDR, controlRegister, _buffer + i, I2C_BUFFER_SIZE); 648 | }*/ 649 | 650 | return EXIT_SUCCESS; 651 | } 652 | 653 | // Write a character to the buffer 654 | int oledPrintChar(char c) 655 | { 656 | int status; 657 | int charIndex = (int) c - 32; 658 | 659 | if (c == '\n') { 660 | oledNewLine(); 661 | } else if (charIndex >= 0 && charIndex < sizeof(asciiTable) / sizeof(asciiTable[0])) { 662 | // At the end of the screen 663 | if (_cursor >= OLED_EXP_WIDTH * OLED_EXP_PAGES - 2) { 664 | oledLineScroll(); 665 | } 666 | _cursor++; 667 | if (_cursor % OLED_EXP_WIDTH >= 126) { 668 | _cursor += 2; 669 | } 670 | 671 | memcpy(_buffer + _cursor, asciiTable[charIndex], OLED_EXP_CHAR_LENGTH); 672 | _cursor += OLED_EXP_CHAR_LENGTH; 673 | } 674 | 675 | return status; 676 | } 677 | 678 | int oledLineScroll() 679 | { 680 | memmove(_buffer, _buffer + (OLED_EXP_WIDTH * OLED_EXP_CHAR_LENGTH), OLED_EXP_WIDTH * (OLED_EXP_PAGES - 1) * OLED_EXP_CHAR_LENGTH); 681 | _cursor = OLED_EXP_WIDTH * (OLED_EXP_PAGES - 1); 682 | 683 | return EXIT_SUCCESS; 684 | } 685 | 686 | int oledNewLine() 687 | { 688 | int status; 689 | 690 | return EXIT_SUCCESS; 691 | } 692 | 693 | int oledPrintLine() 694 | { 695 | int status; 696 | 697 | return EXIT_SUCCESS; 698 | } 699 | 700 | -------------------------------------------------------------------------------- /src/pwm-exp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | // initialize the pwm info struct 5 | void _initPwmSetup(struct pwmSetup *obj) 6 | { 7 | obj->driverNum = -1; 8 | obj->regOffset = 0; 9 | 10 | obj->timeStart = 0; 11 | obj->timeEnd = 0; 12 | 13 | obj->prescale = 0; 14 | } 15 | 16 | // return register offset for specified driver/channel 17 | int _getDriverRegisterOffset (int driverNum, int *addr) 18 | { 19 | // define array of register offsets 20 | int pwmDriverAddr[PWM_EXP_NUM_CHANNELS] = { 21 | PWM_EXP_REG_ADDR_DRIVER0, 22 | PWM_EXP_REG_ADDR_DRIVER1, 23 | PWM_EXP_REG_ADDR_DRIVER2, 24 | PWM_EXP_REG_ADDR_DRIVER3, 25 | PWM_EXP_REG_ADDR_DRIVER4, 26 | PWM_EXP_REG_ADDR_DRIVER5, 27 | PWM_EXP_REG_ADDR_DRIVER6, 28 | PWM_EXP_REG_ADDR_DRIVER7, 29 | PWM_EXP_REG_ADDR_DRIVER8, 30 | PWM_EXP_REG_ADDR_DRIVER9, 31 | PWM_EXP_REG_ADDR_DRIVER10, 32 | PWM_EXP_REG_ADDR_DRIVER11, 33 | PWM_EXP_REG_ADDR_DRIVER12, 34 | PWM_EXP_REG_ADDR_DRIVER13, 35 | PWM_EXP_REG_ADDR_DRIVER14, 36 | PWM_EXP_REG_ADDR_DRIVER15 37 | }; 38 | 39 | // find the address 40 | if (driverNum < 0) { 41 | *addr = PWM_EXP_REG_ADDR_DRIVER_ALL; 42 | } 43 | else if (driverNum < PWM_EXP_NUM_CHANNELS) { 44 | *addr = pwmDriverAddr[driverNum]; 45 | } 46 | else { 47 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:: invalid driver selection, %d\n", driverNum); 48 | return EXIT_FAILURE; 49 | } 50 | 51 | return EXIT_SUCCESS; 52 | } 53 | 54 | // find time count based on duty percentage (0-100) 55 | // return value: the count 56 | int _dutyToCount (float duty) 57 | { 58 | int count; 59 | float dutyDecimal = duty / 100.0f; 60 | 61 | // convert duty to count 62 | count = (int)round(dutyDecimal * (float)PULSE_TOTAL_COUNT); 63 | 64 | // check for negatives 65 | if (count < 0) { 66 | count = 0; 67 | } 68 | 69 | return (count); 70 | } 71 | 72 | // split value into two bytes, and write to the L and H registers (via I2C) 73 | int _writeValue(int addr, int value) 74 | { 75 | int status, byte, wrAddr; 76 | 77 | // write first byte to L 78 | wrAddr = addr + REG_OFFSET_BYTE0; 79 | byte = (value & 0xff); 80 | 81 | //printf("Writing to addr: 0x%02x, data: 0x%02x\n", wrAddr, byte); 82 | status = i2c_write(PWM_I2C_DEVICE_NUM, PWM_I2C_DEVICE_ADDR, wrAddr, byte); 83 | 84 | // write second byte to H 85 | wrAddr = addr + REG_OFFSET_BYTE1; 86 | byte = ((value >> 8) & 0xff); 87 | 88 | //printf("Writing to addr: 0x%02x, data: 0x%02x\n", wrAddr, byte); 89 | status |= i2c_write(PWM_I2C_DEVICE_NUM, PWM_I2C_DEVICE_ADDR, wrAddr, byte); 90 | 91 | return status; 92 | } 93 | 94 | // write ON and OFF time values 95 | int _pwmSetTime(struct pwmSetup *setup) 96 | { 97 | int status; 98 | 99 | // set the ON time 100 | status = _writeValue(setup->regOffset + REG_OFFSET_ON_BYTES, setup->timeStart); 101 | 102 | // set the OFF time 103 | status |= _writeValue(setup->regOffset + REG_OFFSET_OFF_BYTES, setup->timeEnd); 104 | 105 | return (status); 106 | } 107 | 108 | // calculate the ON and OFF time values 109 | void _pwmCalculate(float duty, float delay, struct pwmSetup *setup) 110 | { 111 | int countOn; 112 | int countDelay; 113 | 114 | // to do: add case for 0 duty / 100 duty, 115 | // set the full OFF / full ON bits 116 | 117 | if (duty == 100) { 118 | // set LEDs to FULL_ON 119 | setup->timeStart = LED_FULL_VAL; 120 | setup->timeEnd = 0; 121 | } 122 | else if (duty == 0) { 123 | // set LEDs to FULL_OFF 124 | setup->timeStart = 0; 125 | setup->timeEnd = LED_FULL_VAL; 126 | } 127 | else { 128 | // find duty and delay in terms of numbers 129 | countOn = _dutyToCount(duty); 130 | countDelay = _dutyToCount(delay); 131 | 132 | // calculate the time to assert and deassert the signal 133 | if (countDelay > 0) { 134 | // with delayed start 135 | setup->timeStart = countDelay - 1; 136 | setup->timeEnd = setup->timeStart + countOn; 137 | } 138 | else { 139 | // no delay - start at 0 140 | setup->timeStart = 0; 141 | setup->timeEnd = countOn - 1; 142 | } 143 | 144 | // take care of the case where delay + duty are more than 100 145 | if (setup->timeEnd > PULSE_TOTAL_COUNT) { 146 | setup->timeEnd -= PULSE_TOTAL_COUNT; 147 | } 148 | } 149 | } 150 | 151 | // i2c register writes to set sleep mode 152 | int _pwmSetSleepMode (int bSleepMode) 153 | { 154 | int status; 155 | int addr, val; 156 | 157 | // read MODE1 register 158 | addr = PWM_EXP_REG_MODE1; 159 | status = i2c_readByte ( PWM_I2C_DEVICE_NUM, 160 | PWM_I2C_DEVICE_ADDR, 161 | addr, 162 | &val 163 | ); 164 | if (status == EXIT_FAILURE) { 165 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:_pwmSetSleepMode:: read MODE1 failed\n"); 166 | return EXIT_FAILURE; 167 | } 168 | 169 | // set desired sleep mode 170 | if (bSleepMode == 0) { 171 | // disable SLEEP mode 172 | val &= ~PWM_EXP_REG_MODE1_SLEEP; 173 | } 174 | else { 175 | // enable SLEEP mode 176 | val |= PWM_EXP_REG_MODE1_SLEEP; 177 | } 178 | 179 | // write to MODE1 register 180 | status = i2c_write ( PWM_I2C_DEVICE_NUM, 181 | PWM_I2C_DEVICE_ADDR, 182 | addr, 183 | val 184 | ); 185 | if (status == EXIT_FAILURE) { 186 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:_pwmSetSleepMode:: write to MODE1 failed\n"); 187 | return EXIT_FAILURE; 188 | } 189 | 190 | // wait for the oscillator 191 | usleep(1000); 192 | 193 | return EXIT_SUCCESS; 194 | } 195 | 196 | // i2c register read to find if chip (oscillator) is in sleep mode 197 | int _pwmGetSleepMode (int *bSleepMode) { 198 | int status; 199 | int addr, val; 200 | 201 | // read MODE1 register 202 | addr = PWM_EXP_REG_MODE1; 203 | status = i2c_readByte ( PWM_I2C_DEVICE_NUM, 204 | PWM_I2C_DEVICE_ADDR, 205 | addr, 206 | &val 207 | ); 208 | if (status == EXIT_FAILURE) { 209 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:pwmCheckInit:: read MODE1 failed\n"); 210 | return EXIT_FAILURE; 211 | } 212 | 213 | // check if oscillator is in sleep mode 214 | *bSleepMode = ((val & PWM_EXP_REG_MODE1_SLEEP) == PWM_EXP_REG_MODE1_SLEEP) ? 1 : 0; 215 | 216 | return EXIT_SUCCESS; 217 | } 218 | 219 | // i2c register writes to set sw reset 220 | int _pwmSetReset () 221 | { 222 | int status; 223 | int addr, val; 224 | 225 | // read MODE1 register 226 | addr = PWM_EXP_REG_MODE1; 227 | status = i2c_readByte ( PWM_I2C_DEVICE_NUM, 228 | PWM_I2C_DEVICE_ADDR, 229 | addr, 230 | &val 231 | ); 232 | if (status == EXIT_FAILURE) { 233 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:_pwmSetReset:: read MODE1 register failed\n"); 234 | return EXIT_FAILURE; 235 | } 236 | 237 | // enable reset 238 | val |= PWM_EXP_REG_MODE1_RESET; 239 | status = i2c_write ( PWM_I2C_DEVICE_NUM, 240 | PWM_I2C_DEVICE_ADDR, 241 | addr, 242 | val 243 | ); 244 | if (status == EXIT_FAILURE) { 245 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:_pwmSetReset:: write to MODE1 register failed\n"); 246 | return EXIT_FAILURE; 247 | } 248 | 249 | // wait for the oscillator 250 | usleep(1000); 251 | 252 | return EXIT_SUCCESS; 253 | } 254 | 255 | 256 | // check if the oscillator is running 257 | int pwmCheckInit (int *bInitialized) { 258 | int status; 259 | int bSleepMode; 260 | 261 | // check if oscillator is in sleep mode 262 | status = _pwmGetSleepMode (&bSleepMode); 263 | if (status == EXIT_FAILURE) { 264 | *bInitialized = 0; 265 | return EXIT_FAILURE; 266 | } 267 | 268 | // check if oscillator is in sleep mode 269 | *bInitialized = (bSleepMode == 1 ? 0 : 1); 270 | onionPrint(ONION_SEVERITY_DEBUG, "pwm-exp::pwmCheckInit:: sleep mode is %d, initialized is %d\n", bSleepMode, *bInitialized); 271 | 272 | return status; 273 | } 274 | 275 | // run the initial oscillator setup 276 | int pwmDriverInit () { 277 | int status; 278 | int addr, val; 279 | 280 | onionPrint(ONION_SEVERITY_INFO, "> Initializing PWM Expansion chip\n"); 281 | 282 | // set all channels to 0 283 | pwmSetupDriver(-1, 0, 0); 284 | 285 | // set PWM drivers to totem pole 286 | addr = PWM_EXP_REG_MODE2; 287 | val = PWM_EXP_REG_MODE2_OUTDRV & 0xff; 288 | status = i2c_write ( PWM_I2C_DEVICE_NUM, 289 | PWM_I2C_DEVICE_ADDR, 290 | addr, 291 | val 292 | ); 293 | if (status == EXIT_FAILURE) { 294 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:init:: write to MODE2 failed\n"); 295 | return EXIT_FAILURE; 296 | } 297 | 298 | // enable all call 299 | addr = PWM_EXP_REG_MODE1; 300 | val = PWM_EXP_REG_MODE1_ALLCALL & 0xff; 301 | status = i2c_write ( PWM_I2C_DEVICE_NUM, 302 | PWM_I2C_DEVICE_ADDR, 303 | addr, 304 | val 305 | ); 306 | if (status == EXIT_FAILURE) { 307 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:init:: write to MODE2 failed\n"); 308 | return EXIT_FAILURE; 309 | } 310 | 311 | // wait for oscillator 312 | usleep(1000); 313 | 314 | //// reset MODE1 (without sleep) 315 | // disable SLEEP mode 316 | status = _pwmSetSleepMode(0); 317 | if (status == EXIT_FAILURE) { 318 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:init:: disabling SLEEP mode failed\n"); 319 | return EXIT_FAILURE; 320 | } 321 | 322 | // enable the reset 323 | status = _pwmSetReset(); 324 | if (status == EXIT_FAILURE) { 325 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:init:: reset failed\n"); 326 | return EXIT_FAILURE; 327 | } 328 | 329 | return EXIT_SUCCESS; 330 | } 331 | 332 | // disable the chip - set oscillator to sleep 333 | int pwmDisableChip () 334 | { 335 | int status; 336 | 337 | onionPrint(ONION_SEVERITY_INFO, "> Oscillator going into sleep mode, PWM disabled\n"); 338 | 339 | // enable oscillator sleep mode 340 | status = _pwmSetSleepMode(1); 341 | if (status == EXIT_FAILURE) { 342 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:pwmSetFreq:: disabling SLEEP mode failed\n"); 343 | return EXIT_FAILURE; 344 | } 345 | 346 | return EXIT_SUCCESS; 347 | } 348 | 349 | // program the prescale value for desired pwm frequency 350 | int pwmSetFrequency(float freq) 351 | { 352 | int status; 353 | int prescale; 354 | int addr, val; 355 | 356 | //// calculate the prescale value 357 | // prescale = round( osc_clk / pulse_count x update_rate ) - 1 358 | prescale = (int)round( (float)OSCILLATOR_CLOCK/((float)PULSE_TOTAL_COUNT * freq) ) - 1; 359 | 360 | // clamp the value 361 | if (prescale < PRESCALE_MIN_VALUE) { 362 | prescale = PRESCALE_MIN_VALUE; 363 | } 364 | else if (prescale > PRESCALE_MAX_VALUE) { 365 | prescale = PRESCALE_MAX_VALUE; 366 | } 367 | 368 | // read current prescale value 369 | addr = PWM_EXP_REG_ADDR_PRESCALE; 370 | status = i2c_readByte ( PWM_I2C_DEVICE_NUM, 371 | PWM_I2C_DEVICE_ADDR, 372 | addr, 373 | &val 374 | ); 375 | if (status == EXIT_FAILURE) { 376 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:pwmSetFreq:: read PRESCALE failed\n"); 377 | return EXIT_FAILURE; 378 | } 379 | 380 | // only program frequency if new frequency is required 381 | if (prescale != val) 382 | { 383 | onionPrint(ONION_SEVERITY_INFO, "> Setting signal frequency to %0.2f Hz (prescale: 0x%02x)\n", freq, prescale); 384 | 385 | //// Go to sleep 386 | // read MODE1 register 387 | addr = PWM_EXP_REG_MODE1; 388 | status = i2c_readByte ( PWM_I2C_DEVICE_NUM, 389 | PWM_I2C_DEVICE_ADDR, 390 | addr, 391 | &val 392 | ); 393 | if (status == EXIT_FAILURE) { 394 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:pwmSetFreq:: read MODE1 failed\n"); 395 | return EXIT_FAILURE; 396 | } 397 | 398 | // enable sleep mode to disable the oscillator 399 | status = _pwmSetSleepMode(1); 400 | if (status == EXIT_FAILURE) { 401 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:pwmSetFreq:: disabling SLEEP mode failed\n"); 402 | return EXIT_FAILURE; 403 | } 404 | 405 | // set the prescale value 406 | addr = PWM_EXP_REG_ADDR_PRESCALE; 407 | status = i2c_write ( PWM_I2C_DEVICE_NUM, 408 | PWM_I2C_DEVICE_ADDR, 409 | addr, 410 | prescale 411 | ); 412 | if (status == EXIT_FAILURE) { 413 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:pwmSetFreq:: setting prescale value failed\n"); 414 | return EXIT_FAILURE; 415 | } 416 | 417 | //// wake up - reset sleep 418 | // disable sleep mode to enable the oscillator 419 | status = _pwmSetSleepMode(0); 420 | if (status == EXIT_FAILURE) { 421 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:pwmSetFreq:: disabling SLEEP mode failed\n"); 422 | return EXIT_FAILURE; 423 | } 424 | 425 | // reset 426 | status = _pwmSetReset(); 427 | if (status == EXIT_FAILURE) { 428 | onionPrint(ONION_SEVERITY_FATAL, "pwm-exp:pwmSetFreq:: reset failed\n"); 429 | return EXIT_FAILURE; 430 | } 431 | } 432 | 433 | return status; 434 | } 435 | 436 | // perform PWM driver setup based on duty and delay 437 | int pwmSetupDriver(int driverNum, float duty, float delay) 438 | { 439 | int status; 440 | struct pwmSetup setup; 441 | 442 | // initialize the setup structure 443 | _initPwmSetup(&setup); 444 | 445 | // find driver number and then register offset 446 | setup.driverNum = driverNum; 447 | status = _getDriverRegisterOffset( driverNum, &(setup.regOffset) ); 448 | 449 | // find on and off times 450 | _pwmCalculate(duty, delay, &setup); 451 | 452 | onionPrint(ONION_SEVERITY_INFO, "> Generating PWM signal with %0.2f%% duty cycle (%0.2f%% delay)\n", duty, delay); 453 | //printf("PWM: start: %d (0x%04x), stop: %d (0x%04x)\n\n", setup.timeStart, setup.timeStart, setup.timeEnd, setup.timeEnd); 454 | 455 | // write on and off times via i2c 456 | if (status == EXIT_SUCCESS) { 457 | status = _pwmSetTime(&setup); 458 | } 459 | 460 | return status; 461 | } 462 | 463 | 464 | -------------------------------------------------------------------------------- /src/python/oled-exp-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | // static object variable for error: 6 | static PyObject *PyOledExpError; 7 | 8 | static char *wrmsg_i2c = "I2C transaction failed."; 9 | static char *wrmsg_file = "Reading image file failed."; 10 | 11 | 12 | /* 13 | * Python Analogue for setting verbosity 14 | */ 15 | static PyObject* pyOledSetVerbosity(PyObject* self, PyObject* args) 16 | { 17 | int verbosity; 18 | 19 | // parse the arguments 20 | if (!PyArg_ParseTuple(args, "i", &verbosity) ) { 21 | return NULL; 22 | } 23 | 24 | // make the call 25 | onionSetVerbosity (verbosity); 26 | 27 | return Py_BuildValue("i", 0); 28 | } 29 | 30 | /* 31 | * oledDriverInit() Python Analogue 32 | */ 33 | static PyObject* pyOledDriverInit(PyObject* self, PyObject* args) 34 | { 35 | int status; 36 | 37 | // make the oled-exp call 38 | status = oledDriverInit (); 39 | 40 | if (status != EXIT_SUCCESS) { 41 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 42 | return NULL; 43 | } 44 | 45 | return Py_BuildValue("i", status); 46 | } 47 | 48 | /* 49 | * oledClear() Python Analogue 50 | */ 51 | static PyObject* pyOledClear(PyObject* self, PyObject* args) 52 | { 53 | int status; 54 | 55 | // make the oled-exp call 56 | status = oledClear (); 57 | 58 | if (status != EXIT_SUCCESS) { 59 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 60 | return NULL; 61 | } 62 | 63 | return Py_BuildValue("i", status); 64 | } 65 | 66 | 67 | ////// Configuration Functions ////// 68 | /* 69 | * oledSetDisplayPower() Python Analogue 70 | */ 71 | static PyObject* pyOledSetDisplayPower(PyObject* self, PyObject* args) 72 | { 73 | int status, bOn; 74 | 75 | // parse the arguments 76 | if (!PyArg_ParseTuple(args, "i", &bOn) ) { 77 | return NULL; 78 | } 79 | 80 | // make the oled-exp call 81 | status = oledSetDisplayPower (bOn); 82 | 83 | if (status != EXIT_SUCCESS) { 84 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 85 | return NULL; 86 | } 87 | 88 | return Py_BuildValue("i", status); 89 | } 90 | 91 | /* 92 | * oledSetDisplayMode() Python Analogue 93 | */ 94 | static PyObject* pyOledSetDisplayMode(PyObject* self, PyObject* args) 95 | { 96 | int status, bInvert; 97 | 98 | // parse the arguments 99 | if (!PyArg_ParseTuple(args, "i", &bInvert) ) { 100 | return NULL; 101 | } 102 | 103 | // make the oled-exp call 104 | status = oledSetDisplayMode (bInvert); 105 | 106 | if (status != EXIT_SUCCESS) { 107 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 108 | return NULL; 109 | } 110 | 111 | return Py_BuildValue("i", status); 112 | } 113 | 114 | /* 115 | * oledSetBrightness() Python Analogue 116 | */ 117 | static PyObject* pyOledSetBrightness(PyObject* self, PyObject* args) 118 | { 119 | int status, brightness; 120 | 121 | // parse the arguments 122 | if (!PyArg_ParseTuple(args, "i", &brightness) ) { 123 | return NULL; 124 | } 125 | 126 | // make the oled-exp call 127 | status = oledSetBrightness (brightness); 128 | 129 | if (status != EXIT_SUCCESS) { 130 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 131 | return NULL; 132 | } 133 | 134 | return Py_BuildValue("i", status); 135 | } 136 | 137 | /* 138 | * oledSetDim() Python Analogue 139 | */ 140 | static PyObject* pyOledSetDim(PyObject* self, PyObject* args) 141 | { 142 | int status, bDim; 143 | 144 | // parse the arguments 145 | if (!PyArg_ParseTuple(args, "i", &bDim) ) { 146 | return NULL; 147 | } 148 | 149 | // make the oled-exp call 150 | status = oledSetDim (bDim); 151 | 152 | if (status != EXIT_SUCCESS) { 153 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 154 | return NULL; 155 | } 156 | 157 | return Py_BuildValue("i", status); 158 | } 159 | 160 | /* 161 | * oledSetMemoryMode() Python Analogue 162 | */ 163 | static PyObject* pyOledSetMemoryMode(PyObject* self, PyObject* args) 164 | { 165 | int status, mode; 166 | 167 | // parse the arguments 168 | if (!PyArg_ParseTuple(args, "i", &mode) ) { 169 | return NULL; 170 | } 171 | 172 | // make the oled-exp call 173 | status = oledSetMemoryMode (mode); 174 | 175 | if (status != EXIT_SUCCESS) { 176 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 177 | return NULL; 178 | } 179 | 180 | return Py_BuildValue("i", status); 181 | } 182 | 183 | 184 | /* 185 | * oledSetCursor() Python Analogue 186 | */ 187 | static PyObject* pyOledSetCursor(PyObject* self, PyObject* args) 188 | { 189 | int status, row, col; 190 | 191 | // parse the arguments 192 | if (!PyArg_ParseTuple(args, "ii", &row, &col) ) { 193 | return NULL; 194 | } 195 | 196 | // make the oled-exp call 197 | status = oledSetCursor (row, col); 198 | 199 | if (status != EXIT_SUCCESS) { 200 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 201 | return NULL; 202 | } 203 | 204 | return Py_BuildValue("i", status); 205 | } 206 | 207 | /* 208 | * oledSetCursorByPixel() Python Analogue 209 | */ 210 | static PyObject* pyOledSetCursorByPixel(PyObject* self, PyObject* args) 211 | { 212 | int status, row, pixel; 213 | 214 | // parse the arguments 215 | if (!PyArg_ParseTuple(args, "ii", &row, &pixel) ) { 216 | return NULL; 217 | } 218 | 219 | // make the oled-exp call 220 | status = oledSetCursorByPixel (row, pixel); 221 | 222 | if (status != EXIT_SUCCESS) { 223 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 224 | return NULL; 225 | } 226 | 227 | return Py_BuildValue("i", status); 228 | } 229 | 230 | /* 231 | * oledSetColumnAddressing() Python Analogue 232 | */ 233 | static PyObject* pyOledSetColumnAddressing(PyObject* self, PyObject* args) 234 | { 235 | int status, startPixel, endPixel; 236 | 237 | // parse the arguments 238 | if (!PyArg_ParseTuple(args, "ii", &startPixel, &endPixel) ) { 239 | return NULL; 240 | } 241 | 242 | // make the oled-exp call 243 | status = oledSetColumnAddressing (startPixel, endPixel); 244 | 245 | if (status != EXIT_SUCCESS) { 246 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 247 | return NULL; 248 | } 249 | 250 | return Py_BuildValue("i", status); 251 | } 252 | 253 | /* 254 | * oledSetTextColumns() Python Analogue 255 | */ 256 | static PyObject* pyOledSetTextColumns(PyObject* self, PyObject* args) 257 | { 258 | int status, startPixel, endPixel; 259 | 260 | // make the oled-exp call 261 | status = oledSetTextColumns (); 262 | 263 | if (status != EXIT_SUCCESS) { 264 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 265 | return NULL; 266 | } 267 | 268 | return Py_BuildValue("i", status); 269 | } 270 | 271 | /* 272 | * oledSetImageColumns() Python Analogue 273 | */ 274 | static PyObject* pyOledSetImageColumns(PyObject* self, PyObject* args) 275 | { 276 | int status, startPixel, endPixel; 277 | 278 | // make the oled-exp call 279 | status = oledSetImageColumns (); 280 | 281 | if (status != EXIT_SUCCESS) { 282 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 283 | return NULL; 284 | } 285 | 286 | return Py_BuildValue("i", status); 287 | } 288 | 289 | 290 | 291 | ////// Display Functions ////// 292 | /* 293 | * oledWriteChar() Python Analogue 294 | */ 295 | static PyObject* pyOledWriteChar(PyObject* self, PyObject* args) 296 | { 297 | int status; 298 | const char *msg; 299 | char c; 300 | 301 | // parse the arguments 302 | if (!PyArg_ParseTuple(args, "s", &msg) ) { 303 | return NULL; 304 | } 305 | 306 | c = (char)msg[0]; 307 | 308 | // make the oled-exp call 309 | status = oledWriteChar (c); 310 | 311 | if (status != EXIT_SUCCESS) { 312 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 313 | return NULL; 314 | } 315 | 316 | return Py_BuildValue("i", status); 317 | } 318 | 319 | /* 320 | * oledWrite() Python Analogue 321 | */ 322 | static PyObject* pyOledWrite(PyObject* self, PyObject* args) 323 | { 324 | int status; 325 | const char *msg; 326 | 327 | // parse the arguments 328 | if (!PyArg_ParseTuple(args, "s", &msg) ) { 329 | return NULL; 330 | } 331 | 332 | // make the oled-exp call 333 | status = oledWrite ((char*)msg); 334 | 335 | if (status != EXIT_SUCCESS) { 336 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 337 | return NULL; 338 | } 339 | 340 | return Py_BuildValue("i", status); 341 | } 342 | 343 | /* 344 | * oledWrite() Python Analogue 345 | */ 346 | static PyObject* pyOledWriteByte(PyObject* self, PyObject* args) 347 | { 348 | int status; 349 | int byte; 350 | 351 | // parse the arguments 352 | if (!PyArg_ParseTuple(args, "i", &byte) ) { 353 | return NULL; 354 | } 355 | 356 | // make the oled-exp call 357 | status = oledWriteByte (byte); 358 | 359 | if (status != EXIT_SUCCESS) { 360 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 361 | return NULL; 362 | } 363 | 364 | return Py_BuildValue("i", status); 365 | } 366 | 367 | /* 368 | * DIFFERENT FROM C LIB: 369 | * oledDrawFromFile() 370 | */ 371 | static PyObject* pyOledDrawFromFile(PyObject* self, PyObject* args) 372 | { 373 | int status; 374 | uint8_t *buffer; 375 | const char *filename; 376 | 377 | // parse the arguments 378 | if (!PyArg_ParseTuple(args, "s", &filename) ) { 379 | return NULL; 380 | } 381 | 382 | // allocate memory for the buffer 383 | buffer = malloc(OLED_EXP_WIDTH*OLED_EXP_HEIGHT/8 * sizeof *buffer); 384 | 385 | // read LCD data from file 386 | status = oledReadLcdFile ((char*)filename, buffer); 387 | 388 | if (status != EXIT_SUCCESS) { 389 | PyErr_SetString(PyExc_IOError, wrmsg_file); 390 | return NULL; 391 | } 392 | 393 | // draw data onto screen 394 | status = oledDraw (buffer, OLED_EXP_WIDTH*OLED_EXP_HEIGHT/8); 395 | 396 | if (status != EXIT_SUCCESS) { 397 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 398 | return NULL; 399 | } 400 | 401 | 402 | return Py_BuildValue("i", status); 403 | } 404 | 405 | // LAZAR: implement draw from buffer functions 406 | 407 | 408 | 409 | 410 | ////// Scrolling Functions ////// 411 | /* 412 | * oledScroll() Python Analogue 413 | */ 414 | static PyObject* pyOledScroll(PyObject* self, PyObject* args) 415 | { 416 | int status; 417 | int direction, scrollSpeed, startPage, stopPage; 418 | 419 | // parse the arguments 420 | if (!PyArg_ParseTuple(args, "iiii", &direction, &scrollSpeed, &startPage, &stopPage) ) { 421 | return NULL; 422 | } 423 | 424 | // make the oled-exp call 425 | status = oledScroll (direction, scrollSpeed, startPage, stopPage); 426 | 427 | if (status != EXIT_SUCCESS) { 428 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 429 | return NULL; 430 | } 431 | 432 | return Py_BuildValue("i", status); 433 | } 434 | 435 | /* 436 | * oledScrollDiagonal() Python Analogue 437 | */ 438 | static PyObject* pyOledScrollDiagonal(PyObject* self, PyObject* args) 439 | { 440 | int status; 441 | int direction, scrollSpeed, fixedRows, scrollRows, verticalOffset, startPage, stopPage; 442 | 443 | // parse the arguments 444 | if (!PyArg_ParseTuple(args, "iiiiiii", &direction, &scrollSpeed, &fixedRows, &scrollRows, &verticalOffset, &startPage, &stopPage) ) { 445 | return NULL; 446 | } 447 | 448 | // make the oled-exp call 449 | status = oledScrollDiagonal (direction, scrollSpeed, fixedRows, scrollRows, verticalOffset, startPage, stopPage); 450 | 451 | if (status != EXIT_SUCCESS) { 452 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 453 | return NULL; 454 | } 455 | 456 | return Py_BuildValue("i", status); 457 | } 458 | 459 | /* 460 | * oledScrollStop() Python Analogue 461 | */ 462 | static PyObject* pyOledScrollStop(PyObject* self, PyObject* args) 463 | { 464 | int status; 465 | 466 | // make the oled-exp call 467 | status = oledScrollStop (); 468 | 469 | if (status != EXIT_SUCCESS) { 470 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 471 | return NULL; 472 | } 473 | 474 | return Py_BuildValue("i", status); 475 | } 476 | 477 | 478 | 479 | 480 | ////// Python Module Setup ////// 481 | /* 482 | * Bind Python function names to the C functions 483 | */ 484 | static PyMethodDef pyOledExpMethods[] = { 485 | {"setVerbosity", pyOledSetVerbosity, METH_VARARGS}, 486 | 487 | {"driverInit", pyOledDriverInit, METH_VARARGS}, 488 | 489 | {"clear", pyOledClear, METH_VARARGS}, 490 | {"setDisplayPower", pyOledSetDisplayPower, METH_VARARGS}, 491 | {"setDisplayMode", pyOledSetDisplayMode, METH_VARARGS}, 492 | {"setBrightness", pyOledSetBrightness, METH_VARARGS}, 493 | {"setDim", pyOledSetDim, METH_VARARGS}, 494 | {"setMemoryMode", pyOledSetMemoryMode, METH_VARARGS}, 495 | 496 | {"setCursor", pyOledSetCursor, METH_VARARGS}, 497 | {"setCursorByPixel", pyOledSetCursorByPixel, METH_VARARGS}, 498 | 499 | {"setColumnAddressing", pyOledSetColumnAddressing, METH_VARARGS}, 500 | {"setImageColumns", pyOledSetImageColumns, METH_VARARGS}, 501 | {"setTextColumns", pyOledSetTextColumns, METH_VARARGS}, 502 | 503 | {"writeChar", pyOledWriteChar, METH_VARARGS}, 504 | {"write", pyOledWrite, METH_VARARGS}, 505 | {"writeByte", pyOledWriteByte, METH_VARARGS}, 506 | 507 | {"drawFromFile", pyOledDrawFromFile, METH_VARARGS}, 508 | 509 | {"scroll", pyOledScroll, METH_VARARGS}, 510 | {"scrollDiagonal", pyOledScrollDiagonal, METH_VARARGS}, 511 | {"scrollStop", pyOledScrollStop, METH_VARARGS}, 512 | 513 | {NULL, NULL} /* Sentinel */ 514 | }; 515 | 516 | #if PY_MAJOR_VERSION >= 3 517 | static struct PyModuleDef oledExp = { 518 | PyModuleDef_HEAD_INIT, 519 | "relayExp", 520 | "Onion OLED Expansion Module", 521 | -1, 522 | pyOledExpMethods, 523 | NULL, NULL, NULL, NULL 524 | }; 525 | #endif 526 | 527 | PyObject * PyInit_oledExp(void) 528 | { 529 | #if PY_MAJOR_VERSION >= 3 530 | return PyModule_Create(&oledExp); 531 | #else 532 | return Py_InitModule("oledExp", pyOledExpMethods); 533 | #endif 534 | } 535 | 536 | /* 537 | * Python calls this to initialize this module 538 | */ 539 | void initoledExp() 540 | { 541 | PyObject *m; 542 | 543 | m = PyInit_oledExp(); 544 | if (m == NULL) 545 | return; 546 | 547 | PyOledExpError = PyErr_NewException("oledExp.error", NULL, NULL); 548 | Py_INCREF(PyOledExpError); 549 | PyModule_AddObject(m, "error", PyOledExpError); 550 | } 551 | -------------------------------------------------------------------------------- /src/python/omegaMotors.py: -------------------------------------------------------------------------------- 1 | from OmegaExpansion import pwmExp 2 | 3 | 4 | __version__ = "0.1" 5 | 6 | 7 | SERVO_FREQUENCY = 50 8 | SERVO_MIN_PULSE = 600 # 600us = 0.6ms 9 | SERVO_MAX_PULSE = 2400 # 2400us = 2.4ms 10 | 11 | H_BRIDGE_MOTOR_FORWARD = 0 12 | H_BRIDGE_MOTOR_REVERSE = 1 13 | 14 | class OmegaPwm: 15 | """Base class for PWM signal""" 16 | 17 | def __init__(self, channel): 18 | self.channel = channel 19 | self.frequency = SERVO_FREQUENCY 20 | 21 | # check that pwm-exp has been initialized 22 | bInit = pwmExp.checkInit() 23 | 24 | if (bInit == 0): 25 | # initialize the expansion 26 | ret = pwmExp.driverInit() 27 | if (ret != 0): 28 | print 'ERROR: pwm-exp init not successful!' 29 | 30 | # set to default frequency 31 | self._setFrequency(self.frequency) 32 | 33 | 34 | def _setFrequency(self, freq): 35 | """Set frequency of pwm-exp oscillator""" 36 | self.frequency = freq 37 | ret = pwmExp.setFrequency(freq); 38 | if (ret != 0): 39 | print 'ERROR: pwm-exp setFrequency not successful!' 40 | 41 | return ret 42 | 43 | def getFrequency(self): 44 | """Get frequency of pwm-exp oscillator""" 45 | return self.frequency 46 | 47 | def setDutyCycle(self, duty): 48 | """Set duty cycle for pwm channel""" 49 | ret = pwmExp.setupDriver(self.channel, duty, 0) 50 | if (ret != 0): 51 | print 'ERROR: pwm-exp setupDriver not successful!' 52 | 53 | return ret 54 | 55 | 56 | class DigitalPin: 57 | """Class that uses PWM signals as 0 or 1""" 58 | 59 | def __init__(self, channel): 60 | # initialize a pwm channel 61 | self.channel = channel 62 | self.pwmDriver = OmegaPwm(self.channel) 63 | 64 | def set(self, value): 65 | """Set the PWM signal to either OFF (0%) or ON (100%)""" 66 | if (value == 0): 67 | duty = 0 68 | else: 69 | duty = 100 70 | 71 | ret = self.pwmDriver.setDutyCycle(duty) 72 | return ret 73 | 74 | def turnOff(self): 75 | """Set the PWM signal to OFF (0%)""" 76 | ret = self.set(0) 77 | return ret 78 | 79 | def turnOn(self): 80 | """Set the PWM signal to ON (100%)""" 81 | ret = self.set(100) 82 | return ret 83 | 84 | 85 | class Servo: 86 | """Class that uses PWM signals to drive a servo""" 87 | 88 | def __init__(self, channel, minPulse, maxPulse): 89 | # initialize a pwm channel 90 | self.channel = channel 91 | self.pwmDriver = OmegaPwm(self.channel) 92 | 93 | # note the min and max pulses (in microseconds) 94 | self.minPulse = minPulse 95 | self.maxPulse = maxPulse 96 | 97 | # calculate the total range 98 | self.range = self.maxPulse - self.minPulse 99 | 100 | # calculate the us / degree 101 | self.step = self.range / float(180) 102 | 103 | # calculate the period (in us) 104 | self.period = (1000000 / self.pwmDriver.getFrequency()) 105 | 106 | # initialize the min and max angles 107 | self.minAngle = 0 108 | self.maxAngle = 180 109 | 110 | def getSettings(self): 111 | """Return an object with all settings""" 112 | settings = {} 113 | settings['channel'] = self.channel 114 | settings['minPulse'] = self.minPulse 115 | settings['maxPulse'] = self.maxPulse 116 | 117 | settings['range'] = self.range 118 | settings['step'] = self.step 119 | settings['period'] = self.period 120 | 121 | settings['minAngle'] = self.minAngle 122 | settings['maxAngle'] = self.maxAngle 123 | 124 | return settings 125 | 126 | def setupMinAngle(self, angle): 127 | """Program a minimum angle""" 128 | self.minAngle = angle 129 | 130 | def setupMaxAngle(self, angle): 131 | """Program a maximum angle""" 132 | self.maxAngle = angle 133 | 134 | def setAngle(self, angle): 135 | """Move the servo to the specified angle""" 136 | # check against the minimum and maximium angles 137 | if angle < self.minAngle: 138 | angle = self.minAngle 139 | elif angle > self.maxAngle: 140 | angle = self.maxAngle 141 | 142 | # calculate pulse width for this angle 143 | pulseWidth = angle * self.step + self.minPulse 144 | 145 | # find the duty cycle percentage of the pulse width 146 | duty = (pulseWidth * 100) / float(self.period) 147 | 148 | # program the duty cycle 149 | ret = self.pwmDriver.setDutyCycle(duty) 150 | return ret 151 | 152 | class hBridgeMotor: 153 | """Class that two digital signals and a pwm signal to control an h-bridge""" 154 | 155 | def __init__(self, pwmChannel, fwdChannel, revChannel): 156 | # note the channels 157 | self.pwmChannel = pwmChannel 158 | self.fwdChannel = fwdChannel 159 | self.revChannel = revChannel 160 | 161 | # setup the objects 162 | self.pwmDriver = OmegaPwm(self.pwmChannel) 163 | self.pwmDriver.setDutyCycle(0) 164 | self.fwdDriver = DigitalPin(self.fwdChannel) 165 | self.fwdDriver.turnOff() 166 | self.revDriver = DigitalPin(self.revChannel) 167 | self.revDriver.turnOff() 168 | 169 | # setup the limitations 170 | self.minDuty = 0 171 | self.maxDuty = 100 172 | 173 | def setupMinDuty(self, duty): 174 | """Set the minimum allowed duty cycle for pwm""" 175 | self.minDuty = duty 176 | 177 | def setupMaxDuty(self, duty): 178 | """Set the maximum allowed duty cycle for pwm""" 179 | self.maxDuty = duty 180 | 181 | def reset(self): 182 | """Set the PWM to 0%, disable both h-bridge controls""" 183 | ret = self.pwmDriver.setDutyCycle(0) 184 | ret |= self.fwdDriver.turnOff() 185 | ret |= self.revDriver.turnOff() 186 | 187 | return ret 188 | 189 | def spin(self, direction, duty): 190 | """Set the PWM to the specified duty, and in the specified direction""" 191 | ret = 0 192 | 193 | # 0 - forward, 1 - reverse 194 | if (direction == H_BRIDGE_MOTOR_FORWARD): 195 | self.revDriver.turnOff() 196 | self.fwdDriver.turnOn() 197 | elif (direction == H_BRIDGE_MOTOR_REVERSE): 198 | self.fwdDriver.turnOff() 199 | self.revDriver.turnOn() 200 | else: 201 | ret = -1 202 | 203 | if (ret == 0): 204 | # check against the minimum and maximium pwm 205 | if duty < self.minDuty: 206 | duty = self.minDuty 207 | elif duty > self.maxDuty: 208 | duty = self.maxDuty 209 | 210 | # program the duty cycle 211 | ret = self.pwmDriver.setDutyCycle(duty) 212 | return ret 213 | 214 | def spinForward(self, duty): 215 | ret = self.drive(H_BRIDGE_MOTOR_FORWARD, duty) 216 | return ret 217 | 218 | def spinReverse(self, duty): 219 | ret = self.drive(H_BRIDGE_MOTOR_REVERSE, duty) 220 | return ret 221 | 222 | 223 | -------------------------------------------------------------------------------- /src/python/onion-i2c-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if PY_MAJOR_VERSION < 3 5 | #define PyLong_AS_LONG(val) PyInt_AS_LONG(val) 6 | #define PyLong_AsLong(val) PyInt_AsLong(val) 7 | #endif 8 | 9 | // Macros needed for Python 3 10 | #ifndef PyInt_Check 11 | #define PyInt_Check PyLong_Check 12 | #define PyInt_FromLong PyLong_FromLong 13 | #define PyInt_AsLong PyLong_AsLong 14 | #define PyInt_Type PyLong_Type 15 | #endif 16 | 17 | 18 | // static object variable for error: 19 | static PyObject *PyOnionI2CError; 20 | 21 | 22 | PyDoc_STRVAR(onionI2C_module_doc, 23 | "This module defines an object type that performs I2c transactions\n" 24 | "using the Linux sysfs interface\n"); 25 | 26 | 27 | typedef struct { 28 | PyObject_HEAD 29 | 30 | int adapterNum; 31 | } OnionI2CObject; 32 | 33 | // required class functions 34 | static PyObject * 35 | onionI2C_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 36 | { 37 | OnionI2CObject *self; 38 | if ((self = (OnionI2CObject *)type->tp_alloc(type, 0)) == NULL) 39 | return NULL; 40 | 41 | // setup the params to the default 42 | self->adapterNum = I2C_DEFAULT_ADAPTER; 43 | 44 | Py_INCREF(self); 45 | return (PyObject *)self; 46 | } 47 | 48 | static int 49 | onionI2C_init(OnionI2CObject *self, PyObject *args, PyObject *kwds) 50 | { 51 | int adapter = -1; 52 | static char *kwlist[] = {"adapter", NULL}; 53 | 54 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:__init__", kwlist, &adapter) ) { 55 | return -1; 56 | } 57 | 58 | if (adapter >= 0) { 59 | self->adapterNum = adapter; 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | 66 | static PyObject * 67 | onionI2C_close(OnionI2CObject *self) 68 | { 69 | // reset the params 70 | self->adapterNum = -1; 71 | 72 | Py_INCREF(Py_None); 73 | return Py_None; 74 | } 75 | 76 | static void 77 | onionI2C_dealloc(OnionI2CObject *self) 78 | { 79 | PyObject *ref = onionI2C_close(self); 80 | Py_XDECREF(ref); 81 | 82 | Py_TYPE(self)->tp_free((PyObject *)self); 83 | } 84 | 85 | 86 | 87 | /* 88 | * Functions being mapped to object functions 89 | */ 90 | 91 | static char *wrmsg_list0 = "Empty argument list."; 92 | static char *wrmsg_buffer = "Attempting to read more bytes than I2C max buffer: %d."; 93 | static char *wrmsg_i2c = "I2C transaction failed."; 94 | static char *wrmsg_val = "Non-Int/Long value in arguments: %x."; 95 | 96 | 97 | PyDoc_STRVAR(onionI2C_setVerbosity_doc, 98 | "setVerbosity(level) -> None\n\n" 99 | "Set the verbosity for the object (-1 to 2).\n"); 100 | 101 | static PyObject * 102 | onionI2C_setVerbosity(OnionI2CObject *self, PyObject *args) 103 | { 104 | int verbose; 105 | 106 | // parse the arguments 107 | if (!PyArg_ParseTuple(args, "i", &verbose) ) { 108 | return NULL; 109 | } 110 | 111 | // set the verbosity level 112 | onionSetVerbosity(verbose); 113 | 114 | 115 | Py_INCREF(Py_None); 116 | return Py_None; 117 | } 118 | 119 | 120 | PyDoc_STRVAR(onionI2C_readBytes_doc, 121 | "readBytes(deviceAddr, addr, numBytes) -> [values]\n\n" 122 | "Read 'numBytes' bytes from address 'addr' on I2C device with 'deviceAddr' address.\n"); 123 | 124 | static PyObject * 125 | onionI2C_readBytes(OnionI2CObject *self, PyObject *args) 126 | { 127 | int status, devAddr, addr, bytes, i; 128 | uint8_t buffer[I2C_BUFFER_SIZE]; 129 | PyObject *list; 130 | char wrmsg_text[4096]; 131 | 132 | 133 | // parse the arguments 134 | if (!PyArg_ParseTuple(args, "iii", &devAddr, &addr, &bytes) ) { 135 | return NULL; 136 | } 137 | 138 | // check the number of bytes 139 | if (bytes > I2C_BUFFER_SIZE) { 140 | snprintf(wrmsg_text, sizeof (wrmsg_text) - 1, wrmsg_buffer, I2C_BUFFER_SIZE); 141 | PyErr_SetString(PyExc_OverflowError, wrmsg_text); 142 | return NULL; 143 | } 144 | 145 | // perform the read 146 | status = i2c_read ( self->adapterNum, 147 | devAddr, 148 | addr, 149 | buffer, 150 | bytes 151 | ); 152 | 153 | if (status != EXIT_SUCCESS) { 154 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 155 | return NULL; 156 | } 157 | 158 | // build the python object to be returned from the buffer 159 | list = PyList_New(bytes); 160 | 161 | for (i = 0; i < bytes; i++) { 162 | PyObject *val = Py_BuildValue("l", (long)buffer[i]); 163 | PyList_SET_ITEM(list, i, val); 164 | } 165 | 166 | 167 | return list; 168 | } 169 | 170 | PyDoc_STRVAR(onionI2C_read_doc, 171 | "read(deviceAddr, numBytes) -> [values]\n\n" 172 | "Read 'numBytes' bytes on I2C device with 'deviceAddr' address.\n"); 173 | 174 | static PyObject * 175 | onionI2C_read(OnionI2CObject *self, PyObject *args) 176 | { 177 | int status, devAddr, bytes, i; 178 | uint8_t buffer[I2C_BUFFER_SIZE]; 179 | PyObject *list; 180 | char wrmsg_text[4096]; 181 | 182 | 183 | // parse the arguments 184 | if (!PyArg_ParseTuple(args, "ii", &devAddr, &bytes) ) { 185 | return NULL; 186 | } 187 | 188 | // check the number of bytes 189 | if (bytes > I2C_BUFFER_SIZE) { 190 | snprintf(wrmsg_text, sizeof (wrmsg_text) - 1, wrmsg_buffer, I2C_BUFFER_SIZE); 191 | PyErr_SetString(PyExc_OverflowError, wrmsg_text); 192 | return NULL; 193 | } 194 | 195 | // perform the read 196 | status = i2c_readRaw ( self->adapterNum, 197 | devAddr, 198 | buffer, 199 | bytes 200 | ); 201 | 202 | if (status != EXIT_SUCCESS) { 203 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 204 | return NULL; 205 | } 206 | 207 | // build the python object to be returned from the buffer 208 | list = PyList_New(bytes); 209 | 210 | for (i = 0; i < bytes; i++) { 211 | PyObject *val = Py_BuildValue("l", (long)buffer[i]); 212 | PyList_SET_ITEM(list, i, val); 213 | } 214 | 215 | 216 | return list; 217 | } 218 | 219 | PyDoc_STRVAR(onionI2C_writeBytes_doc, 220 | "writeBytes(deviceAddr, addr, [values]) -> None\n\n" 221 | "Write bytes from 'values' list to address 'addr' on I2C device with 'deviceAddr' address.\n"); 222 | 223 | static PyObject * 224 | onionI2C_writeBytes(OnionI2CObject *self, PyObject *args) 225 | { 226 | int status, devAddr, addr, bytes, i; 227 | uint8_t buffer[I2C_BUFFER_SIZE]; 228 | PyObject *list; 229 | char wrmsg_text[4096]; 230 | 231 | 232 | // parse the arguments 233 | if (!PyArg_ParseTuple(args, "iiO", &devAddr, &addr, &list) ) { 234 | return NULL; 235 | } 236 | 237 | // check the number of bytes 238 | bytes = PyList_Size(list); 239 | if (bytes <= 0) { 240 | PyErr_SetString(PyExc_TypeError, wrmsg_list0); 241 | return NULL; 242 | } 243 | 244 | if (bytes > I2C_BUFFER_SIZE) { 245 | snprintf(wrmsg_text, sizeof (wrmsg_text) - 1, wrmsg_buffer, I2C_BUFFER_SIZE); 246 | PyErr_SetString(PyExc_OverflowError, wrmsg_text); 247 | return NULL; 248 | } 249 | 250 | // populate the values (by iterating through the list) 251 | for (i = 0; i < bytes; i++) { 252 | PyObject *val = PyList_GET_ITEM(list, i); 253 | #if PY_MAJOR_VERSION < 3 254 | if (PyInt_Check(val)) { 255 | buffer[i] = (uint8_t)PyInt_AS_LONG(val); 256 | } else 257 | #endif 258 | { 259 | if (PyLong_Check(val)) { 260 | buffer[i] = (uint8_t)PyLong_AS_LONG(val); 261 | } else { 262 | snprintf(wrmsg_text, sizeof (wrmsg_text) - 1, wrmsg_val, val); 263 | PyErr_SetString(PyExc_TypeError, wrmsg_text); 264 | return NULL; 265 | } 266 | } 267 | } 268 | 269 | // perform the write 270 | status = i2c_writeBuffer ( self->adapterNum, 271 | devAddr, 272 | addr, 273 | buffer, 274 | bytes 275 | ); 276 | 277 | if (status != EXIT_SUCCESS) { 278 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 279 | return NULL; 280 | } 281 | 282 | 283 | Py_INCREF(Py_None); 284 | return Py_None; 285 | } 286 | 287 | PyDoc_STRVAR(onionI2C_writeByte_doc, 288 | "writeByte(deviceAddr, addr, value) -> None\n\n" 289 | "Write a single byte, 'value' to address 'addr' on I2C device with 'deviceAddr' address.\n"); 290 | 291 | static PyObject * 292 | onionI2C_writeByte(OnionI2CObject *self, PyObject *args) 293 | { 294 | int status, devAddr, addr, bytes, value; 295 | uint8_t buffer[I2C_BUFFER_SIZE]; 296 | PyObject *list; 297 | char wrmsg_text[4096]; 298 | 299 | 300 | // parse the arguments 301 | if (!PyArg_ParseTuple(args, "iii", &devAddr, &addr, &value) ) { 302 | return NULL; 303 | } 304 | 305 | // set number of bytes and populate the buffer 306 | bytes = 1; 307 | buffer[0] = value; 308 | 309 | // perform the write 310 | status = i2c_writeBuffer ( self->adapterNum, 311 | devAddr, 312 | addr, 313 | buffer, 314 | bytes 315 | ); 316 | 317 | if (status != EXIT_SUCCESS) { 318 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 319 | return NULL; 320 | } 321 | 322 | 323 | Py_INCREF(Py_None); 324 | return Py_None; 325 | } 326 | 327 | PyDoc_STRVAR(onionI2C_write_doc, 328 | "write(deviceAddr, [values]) -> None\n\n" 329 | "Write a list of bytes 'values' to I2C device with 'deviceAddr' address.\n"); 330 | 331 | static PyObject * 332 | onionI2C_write(OnionI2CObject *self, PyObject *args) 333 | { 334 | int status, devAddr, bytes, i; 335 | uint8_t buffer[I2C_BUFFER_SIZE]; 336 | PyObject *list; 337 | char wrmsg_text[4096]; 338 | 339 | 340 | // parse the arguments 341 | if (!PyArg_ParseTuple(args, "iO", &devAddr, &list) ) { 342 | return NULL; 343 | } 344 | 345 | // check the number of bytes 346 | bytes = PyList_Size(list); 347 | if (bytes <= 0) { 348 | PyErr_SetString(PyExc_TypeError, wrmsg_list0); 349 | return NULL; 350 | } 351 | 352 | if (bytes > I2C_BUFFER_SIZE) { 353 | snprintf(wrmsg_text, sizeof (wrmsg_text) - 1, wrmsg_buffer, I2C_BUFFER_SIZE); 354 | PyErr_SetString(PyExc_OverflowError, wrmsg_text); 355 | return NULL; 356 | } 357 | 358 | // populate the values (by iterating through the list) 359 | for (i = 0; i < bytes; i++) { 360 | PyObject *val = PyList_GET_ITEM(list, i); 361 | #if PY_MAJOR_VERSION < 3 362 | if (PyInt_Check(val)) { 363 | buffer[i] = (uint8_t)PyInt_AS_LONG(val); 364 | } else 365 | #endif 366 | { 367 | if (PyLong_Check(val)) { 368 | buffer[i] = (uint8_t)PyLong_AS_LONG(val); 369 | } else { 370 | snprintf(wrmsg_text, sizeof (wrmsg_text) - 1, wrmsg_val, val); 371 | PyErr_SetString(PyExc_TypeError, wrmsg_text); 372 | return NULL; 373 | } 374 | } 375 | } 376 | 377 | // perform the write (just writing the buffer, no address argument) 378 | status = i2c_writeBufferRaw ( self->adapterNum, 379 | devAddr, 380 | buffer, 381 | bytes 382 | ); 383 | 384 | if (status != EXIT_SUCCESS) { 385 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 386 | return NULL; 387 | } 388 | 389 | 390 | Py_INCREF(Py_None); 391 | return Py_None; 392 | } 393 | 394 | 395 | /* 396 | * Define the get and set functions for the parameters 397 | */ 398 | 399 | // pass in a python value, get a C int back 400 | // returns -1 if conversion failed for whatever reason 401 | static int 402 | onionI2C_convertPyValToInt (PyObject *val) 403 | { 404 | int cValue; 405 | 406 | // check for a valid value 407 | if (val == NULL) { 408 | PyErr_SetString(PyExc_TypeError, 409 | "Cannot delete attribute"); 410 | return -1; 411 | } 412 | 413 | // convert the python value 414 | #if PY_MAJOR_VERSION < 3 415 | if (PyInt_Check(val)) { 416 | cValue = PyInt_AS_LONG(val); 417 | } else 418 | #endif 419 | { 420 | if (PyLong_Check(val)) { 421 | cValue = PyLong_AS_LONG(val); 422 | } else { 423 | PyErr_SetString(PyExc_TypeError, 424 | "The attribute must be an integer"); 425 | return -1; 426 | } 427 | } 428 | 429 | return cValue; 430 | } 431 | 432 | // adapterNum 433 | static PyObject * 434 | onionI2C_get_adapterNum(OnionI2CObject *self, void *closure) 435 | { 436 | // create a python value from the integer 437 | PyObject *result = Py_BuildValue("i", self->adapterNum); 438 | Py_INCREF(result); 439 | return result; 440 | } 441 | 442 | static int 443 | onionI2C_set_adapterNum(OnionI2CObject *self, PyObject *val, void *closure) 444 | { 445 | uint32_t value; 446 | 447 | // convert the python value 448 | value = onionI2C_convertPyValToInt(val); 449 | 450 | if (value != -1) { 451 | self->adapterNum= value; 452 | return 0; 453 | } 454 | 455 | return -1; 456 | } 457 | 458 | static PyGetSetDef onionI2C_getset[] = { 459 | {"adapter", (getter)onionI2C_get_adapterNum, (setter)onionI2C_set_adapterNum, 460 | "I2C adapter number\n"}, 461 | 462 | {NULL} /* Sentinel */ 463 | }; 464 | 465 | 466 | ////// Python Module Setup ////// 467 | /* 468 | * Bind Python function names to the C functions 469 | */ 470 | PyDoc_STRVAR(OnionI2CObjectType_doc, 471 | "onionI2C([adapter]) -> I2C\n\n" 472 | "Return an object that can be used to communicate through a\n" 473 | "I2C device interface.\n"); 474 | 475 | static PyMethodDef onionI2C_methods[] = { 476 | {"setVerbosity", (PyCFunction)onionI2C_setVerbosity, METH_VARARGS, onionI2C_setVerbosity_doc}, 477 | 478 | {"readBytes", (PyCFunction)onionI2C_readBytes, METH_VARARGS, onionI2C_readBytes_doc}, 479 | {"read", (PyCFunction)onionI2C_read, METH_VARARGS, onionI2C_read_doc}, 480 | 481 | {"writeBytes", (PyCFunction)onionI2C_writeBytes, METH_VARARGS, onionI2C_writeBytes_doc}, 482 | {"writeByte", (PyCFunction)onionI2C_writeByte, METH_VARARGS, onionI2C_writeByte_doc}, 483 | {"write", (PyCFunction)onionI2C_write, METH_VARARGS, onionI2C_write_doc}, 484 | 485 | {NULL, NULL} /* Sentinel */ 486 | }; 487 | 488 | 489 | /* 490 | * Python object bindings 491 | */ 492 | static PyTypeObject OnionI2CObjectType = { 493 | #if PY_MAJOR_VERSION >= 3 494 | PyVarObject_HEAD_INIT(NULL, 0) 495 | #else 496 | PyObject_HEAD_INIT(NULL) 497 | 0, /* ob_size */ 498 | #endif 499 | "OnionI2C", /* tp_name */ 500 | sizeof(OnionI2CObject), /* tp_basicsize */ 501 | 0, /* tp_itemsize */ 502 | (destructor)onionI2C_dealloc, /* tp_dealloc */ 503 | 0, /* tp_print */ 504 | 0, /* tp_getattr */ 505 | 0, /* tp_setattr */ 506 | 0, /* tp_compare */ 507 | 0, /* tp_repr */ 508 | 0, /* tp_as_number */ 509 | 0, /* tp_as_sequence */ 510 | 0, /* tp_as_mapping */ 511 | 0, /* tp_hash */ 512 | 0, /* tp_call */ 513 | 0, /* tp_str */ 514 | 0, /* tp_getattro */ 515 | 0, /* tp_setattro */ 516 | 0, /* tp_as_buffer */ 517 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 518 | OnionI2CObjectType_doc, /* tp_doc */ 519 | 0, /* tp_traverse */ 520 | 0, /* tp_clear */ 521 | 0, /* tp_richcompare */ 522 | 0, /* tp_weaklistoffset */ 523 | 0, /* tp_iter */ 524 | 0, /* tp_iternext */ 525 | onionI2C_methods, /* tp_methods */ 526 | 0, /* tp_members */ 527 | onionI2C_getset, /* tp_getset */ 528 | 0, /* tp_base */ 529 | 0, /* tp_dict */ 530 | 0, /* tp_descr_get */ 531 | 0, /* tp_descr_set */ 532 | 0, /* tp_dictoffset */ 533 | (initproc)onionI2C_init, /* tp_init */ 534 | 0, /* tp_alloc */ 535 | onionI2C_new, /* tp_new */ 536 | }; 537 | 538 | static PyMethodDef onionI2C_module_methods[] = { 539 | {NULL} /* Sentinel */ 540 | }; 541 | 542 | #if PY_MAJOR_VERSION >= 3 543 | static struct PyModuleDef moduledef = { 544 | PyModuleDef_HEAD_INIT, 545 | "onionI2C", 546 | onionI2C_module_doc, 547 | -1, 548 | onionI2C_module_methods, 549 | NULL, 550 | NULL, 551 | NULL, 552 | NULL, 553 | }; 554 | #else 555 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 556 | #define PyMODINIT_FUNC void 557 | #endif 558 | #endif 559 | 560 | 561 | 562 | /* 563 | * Python calls this to initialize this module 564 | */ 565 | #if PY_MAJOR_VERSION >= 3 566 | PyMODINIT_FUNC 567 | PyInit_onionI2C(void) 568 | #else 569 | void initonionI2C(void) 570 | #endif 571 | { 572 | PyObject *m; 573 | 574 | if (PyType_Ready(&OnionI2CObjectType) < 0) 575 | #if PY_MAJOR_VERSION >= 3 576 | return NULL; 577 | #else 578 | return; 579 | #endif 580 | 581 | #if PY_MAJOR_VERSION >= 3 582 | m = PyModule_Create(&moduledef); 583 | #else 584 | m = Py_InitModule3("onionI2C", onionI2C_methods, onionI2C_module_doc); 585 | #endif 586 | if (m == NULL) 587 | #if PY_MAJOR_VERSION >= 3 588 | return NULL; 589 | #else 590 | return; 591 | #endif 592 | 593 | Py_INCREF(&OnionI2CObjectType); 594 | PyModule_AddObject(m, "OnionI2C", (PyObject *)&OnionI2CObjectType); 595 | 596 | 597 | PyOnionI2CError = PyErr_NewException("onionI2C.error", NULL, NULL); 598 | Py_INCREF(PyOnionI2CError); 599 | PyModule_AddObject(m, "error", PyOnionI2CError); 600 | 601 | #if PY_MAJOR_VERSION >= 3 602 | return m; 603 | #endif 604 | } 605 | 606 | -------------------------------------------------------------------------------- /src/python/pwm-exp-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | // static object variable for error: 6 | static PyObject *PyPwmExpError; 7 | 8 | static char *wrmsg_i2c = "I2C transaction failed."; 9 | 10 | 11 | /* 12 | * Python Analogue for setting verbosity 13 | */ 14 | static PyObject* pyPwmSetVerbosity(PyObject* self, PyObject* args) 15 | { 16 | int verbosity; 17 | 18 | // parse the arguments 19 | if (!PyArg_ParseTuple(args, "i", &verbosity) ) { 20 | return NULL; 21 | } 22 | 23 | // make the call 24 | onionSetVerbosity (verbosity); 25 | 26 | return Py_BuildValue("i", 0); 27 | } 28 | 29 | /* 30 | * pwmDriverInit() Python Analogue 31 | */ 32 | static PyObject* pyPwmDriverInit(PyObject* self, PyObject* args) 33 | { 34 | int status; 35 | 36 | // make the pwm-exp call 37 | status = pwmDriverInit (); 38 | 39 | if (status != EXIT_SUCCESS) { 40 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 41 | return NULL; 42 | } 43 | 44 | return Py_BuildValue("i", status); 45 | } 46 | 47 | /* 48 | * relayCheckInit() Python Analogue 49 | */ 50 | static PyObject* pyPwmCheckInit(PyObject* self, PyObject* args) 51 | { 52 | int status, bInit; 53 | 54 | // make the pwm-exp call 55 | status = pwmCheckInit (&bInit); 56 | 57 | if (status != EXIT_SUCCESS) { 58 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 59 | return NULL; 60 | } 61 | 62 | return Py_BuildValue("i", bInit); 63 | } 64 | 65 | /* 66 | * pwmDisableChip() Python Analogue 67 | */ 68 | static PyObject* pyPwmDisableChip(PyObject* self, PyObject* args) 69 | { 70 | int status; 71 | 72 | // make the pwm-exp call 73 | status = pwmDisableChip (); 74 | 75 | if (status != EXIT_SUCCESS) { 76 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 77 | return NULL; 78 | } 79 | 80 | return Py_BuildValue("i", status); 81 | } 82 | 83 | /* 84 | * pwmSetFrequency() Python Analogue 85 | */ 86 | static PyObject* pyPwmSetFrequency(PyObject* self, PyObject* args) 87 | { 88 | int status; 89 | float freq; 90 | 91 | // parse the arguments 92 | if (!PyArg_ParseTuple(args, "f", &freq) ) { 93 | return NULL; 94 | } 95 | 96 | // make the pwm-exp call 97 | status = pwmSetFrequency (freq); 98 | 99 | if (status != EXIT_SUCCESS) { 100 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 101 | return NULL; 102 | } 103 | 104 | return Py_BuildValue("i", status); 105 | } 106 | 107 | /* 108 | * relaySetAllChannels() Python Analogue 109 | */ 110 | static PyObject* pyPwmSetupDriver(PyObject* self, PyObject* args) 111 | { 112 | int status, channel; 113 | float duty, delay; 114 | 115 | // parse the arguments 116 | if (!PyArg_ParseTuple(args, "iff", &channel, &duty, &delay) ) { 117 | return NULL; 118 | } 119 | 120 | // make the pwm-exp call 121 | status = pwmSetupDriver (channel, duty, delay); 122 | 123 | if (status != EXIT_SUCCESS) { 124 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 125 | return NULL; 126 | } 127 | 128 | return Py_BuildValue("i", status); 129 | } 130 | 131 | 132 | 133 | ////// Python Module Setup ////// 134 | /* 135 | * Bind Python function names to the C functions 136 | */ 137 | static PyMethodDef pyPwmExpMethods[] = { 138 | {"setVerbosity", pyPwmSetVerbosity, METH_VARARGS}, 139 | {"driverInit", pyPwmDriverInit, METH_VARARGS}, 140 | {"checkInit", pyPwmCheckInit, METH_VARARGS}, 141 | {"disableChip", pyPwmDisableChip, METH_VARARGS}, 142 | {"setFrequency", pyPwmSetFrequency, METH_VARARGS}, 143 | {"setupDriver", pyPwmSetupDriver, METH_VARARGS}, 144 | {NULL, NULL} /* Sentinel */ 145 | }; 146 | 147 | #if PY_MAJOR_VERSION >= 3 148 | static struct PyModuleDef pwmExp = { 149 | PyModuleDef_HEAD_INIT, 150 | "pwmExp", 151 | "Onion PWM Expansion module", 152 | -1, 153 | pyPwmExpMethods, 154 | NULL, NULL, NULL, NULL 155 | }; 156 | #endif 157 | 158 | PyObject * PyInit_pwmExp(void) 159 | { 160 | #if PY_MAJOR_VERSION >= 3 161 | return PyModule_Create(&pwmExp); 162 | #else 163 | return Py_InitModule("pwmExp", pyPwmExpMethods); 164 | #endif 165 | } 166 | 167 | /* 168 | * Python calls this to initialize this module 169 | */ 170 | void initpwmExp() 171 | { 172 | PyObject *m; 173 | 174 | m = PyInit_pwmExp(); 175 | if (m == NULL) 176 | return; 177 | 178 | PyPwmExpError = PyErr_NewException("pwmExp.error", NULL, NULL); 179 | Py_INCREF(PyPwmExpError); 180 | PyModule_AddObject(m, "error", PyPwmExpError); 181 | } 182 | 183 | -------------------------------------------------------------------------------- /src/python/relay-exp-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | // static object variable for error: 6 | static PyObject *PyRelayExpError; 7 | 8 | static char *wrmsg_i2c = "I2C transaction failed."; 9 | 10 | 11 | /* 12 | * Python Analogue for setting verbosity 13 | */ 14 | static PyObject* pyRelaySetVerbosity(PyObject* self, PyObject* args) 15 | { 16 | int verbosity; 17 | 18 | // parse the arguments 19 | if (!PyArg_ParseTuple(args, "i", &verbosity) ) { 20 | return NULL; 21 | } 22 | 23 | // make the call 24 | onionSetVerbosity (verbosity); 25 | 26 | return Py_BuildValue("i", 0); 27 | } 28 | 29 | /* 30 | * relayDriverInit() Python Analogue 31 | */ 32 | static PyObject* pyRelayDriverInit(PyObject* self, PyObject* args) 33 | { 34 | int status, addr; 35 | 36 | // parse the arguments 37 | if (!PyArg_ParseTuple(args, "i", &addr) ) { 38 | return NULL; 39 | } 40 | 41 | // make the relay-exp call 42 | status = relayDriverInit (addr); 43 | 44 | if (status != EXIT_SUCCESS) { 45 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 46 | return NULL; 47 | } 48 | 49 | return Py_BuildValue("i", status); 50 | } 51 | 52 | /* 53 | * relayCheckInit() Python Analogue 54 | */ 55 | static PyObject* pyRelayCheckInit(PyObject* self, PyObject* args) 56 | { 57 | int status, addr, bInit; 58 | 59 | // parse the arguments 60 | if (!PyArg_ParseTuple(args, "i", &addr) ) { 61 | return NULL; 62 | } 63 | 64 | // make the relay-exp call 65 | status = relayCheckInit (addr, &bInit); 66 | 67 | if (status != EXIT_SUCCESS) { 68 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 69 | return NULL; 70 | } 71 | 72 | return Py_BuildValue("i", bInit); 73 | } 74 | 75 | /* 76 | * relaySetChannel() Python Analogue 77 | */ 78 | static PyObject* pyRelaySetChannel(PyObject* self, PyObject* args) 79 | { 80 | int status, addr, channel, state; 81 | 82 | // parse the arguments 83 | if (!PyArg_ParseTuple(args, "iii", &addr, &channel, &state) ) { 84 | return NULL; 85 | } 86 | 87 | // make the relay-exp call 88 | status = relaySetChannel (addr, channel, state); 89 | 90 | if (status != EXIT_SUCCESS) { 91 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 92 | return NULL; 93 | } 94 | 95 | return Py_BuildValue("i", status); 96 | } 97 | 98 | /* 99 | * relaySetAllChannels() Python Analogue 100 | */ 101 | static PyObject* pyRelaySetAllChannels(PyObject* self, PyObject* args) 102 | { 103 | int status, addr, state; 104 | 105 | // parse the arguments 106 | if (!PyArg_ParseTuple(args, "ii", &addr, &state) ) { 107 | return NULL; 108 | } 109 | 110 | // make the relay-exp call 111 | status = relaySetAllChannels (addr, state); 112 | 113 | if (status != EXIT_SUCCESS) { 114 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 115 | return NULL; 116 | } 117 | 118 | return Py_BuildValue("i", status); 119 | } 120 | 121 | 122 | /* 123 | * relayReadChannel() Python Analogue 124 | */ 125 | static PyObject* pyRelayReadChannel(PyObject* self, PyObject* args) 126 | { 127 | int status, addr, channel, state; 128 | 129 | // parse the arguments 130 | if (!PyArg_ParseTuple(args, "ii", &addr, &channel) ) { 131 | return NULL; 132 | } 133 | 134 | // make the relay-exp call 135 | status = relayReadChannel (addr, channel, &state); 136 | 137 | if (status != EXIT_SUCCESS) { 138 | PyErr_SetString(PyExc_IOError, wrmsg_i2c); 139 | return NULL; 140 | } 141 | 142 | return Py_BuildValue("i", state); 143 | } 144 | 145 | 146 | ////// Python Module Setup ////// 147 | /* 148 | * Bind Python function names to the C functions 149 | */ 150 | static PyMethodDef pyRelayExpMethods[] = { 151 | {"setVerbosity", pyRelaySetVerbosity, METH_VARARGS}, 152 | {"driverInit", pyRelayDriverInit, METH_VARARGS}, 153 | {"checkInit", pyRelayCheckInit, METH_VARARGS}, 154 | {"setChannel", pyRelaySetChannel, METH_VARARGS}, 155 | {"setAllChannels", pyRelaySetAllChannels, METH_VARARGS}, 156 | {"readChannel", pyRelayReadChannel, METH_VARARGS}, 157 | {NULL, NULL} /* Sentinel */ 158 | }; 159 | 160 | #if PY_MAJOR_VERSION >= 3 161 | static struct PyModuleDef relayExp = { 162 | PyModuleDef_HEAD_INIT, 163 | "relayExp", 164 | "Onion Relay Expansion Module", 165 | -1, 166 | pyRelayExpMethods, 167 | NULL, NULL, NULL, NULL 168 | }; 169 | #endif 170 | 171 | PyObject * PyInit_relayExp(void) 172 | { 173 | #if PY_MAJOR_VERSION >= 3 174 | return PyModule_Create(&relayExp); 175 | #else 176 | return Py_InitModule("relayExp", pyRelayExpMethods); 177 | #endif 178 | } 179 | 180 | /* 181 | * Python calls this to initialize this module 182 | */ 183 | void initrelayExp() 184 | { 185 | PyObject *m; 186 | 187 | m = PyInit_relayExp(); 188 | if (m == NULL) 189 | return; 190 | 191 | PyRelayExpError = PyErr_NewException("relayExp.error", NULL, NULL); 192 | Py_INCREF(PyRelayExpError); 193 | PyModule_AddObject(m, "error", PyRelayExpError); 194 | } 195 | -------------------------------------------------------------------------------- /src/relay-exp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // perform basic initialization of GPIO chip 4 | int relayDriverInit (int addr) 5 | { 6 | int status; 7 | 8 | onionPrint(ONION_SEVERITY_INFO, "> Initializing Relay Expansion chip\n"); 9 | 10 | // set all GPIOs to output 11 | status = mcp_setAllDirection(addr, 0); 12 | if (status == EXIT_FAILURE) { 13 | onionPrint(ONION_SEVERITY_FATAL, "relay-exp:init:: Setting output direction failed\n"); 14 | return status; 15 | } 16 | 17 | // disable all pullup resistors 18 | status = mcp_setAllPullup(addr, 0); 19 | if (status == EXIT_FAILURE) { 20 | onionPrint(ONION_SEVERITY_FATAL, "relay-exp:init:: Disabling pullup resistors failed\n"); 21 | return status; 22 | } 23 | 24 | // set all GPIOs to 0 25 | status = mcp_setAllGpio(addr, 0); 26 | if (status == EXIT_FAILURE) { 27 | onionPrint(ONION_SEVERITY_FATAL, "relay-exp:init:: Reseting GPIOs failed\n"); 28 | return status; 29 | } 30 | 31 | return status; 32 | } 33 | 34 | // check if GPIO0 and GPIO1 are properly initialized 35 | int relayCheckInit (int addr, int *bInitialized) 36 | { 37 | int status; 38 | int dirGpio0, dirGpio1; 39 | int puGpio0, puGpio1; 40 | 41 | // set default to not initialized 42 | *bInitialized = 0; 43 | 44 | // find GPIO0 direction 45 | status = mcp_getDirection(addr, RELAY_EXP_CHANNEL0, &dirGpio0); 46 | 47 | // exit with failure if read fails 48 | if (status != EXIT_SUCCESS) { 49 | return status; 50 | } 51 | 52 | // find GPIO1 direction 53 | status |= mcp_getDirection(addr, RELAY_EXP_CHANNEL1, &dirGpio1); 54 | 55 | 56 | // find status of GPIO0's pull-up resistor 57 | status |= mcp_getInputPolarity(addr, RELAY_EXP_CHANNEL0, &puGpio0); 58 | 59 | // find status of GPIO1's pull-up resistor 60 | status |= mcp_getInputPolarity(addr, RELAY_EXP_CHANNEL1, &puGpio1); 61 | 62 | 63 | // check for any initialization 64 | if ( status == EXIT_SUCCESS && 65 | dirGpio0 == 0 && 66 | dirGpio1 == 0 && 67 | puGpio0 == 0 && 68 | puGpio1 == 0 69 | ) 70 | { 71 | *bInitialized = 1; 72 | } 73 | 74 | return status; 75 | } 76 | 77 | // read GPIO value, ie read the relay state 78 | int relayReadChannel (int addr, int channel, int* state) 79 | { 80 | int status; 81 | 82 | // read the relay state 83 | status = mcp_getGpio(addr, channel, state); 84 | onionPrint(ONION_SEVERITY_INFO, "> Reading RELAY%d state: %s\n", channel, ( *state == 1 ? "ON" : "OFF") ); 85 | 86 | return status; 87 | } 88 | 89 | // set GPIO value - change the relay state 90 | int relaySetChannel (int addr, int channel, int state) 91 | { 92 | int status; 93 | 94 | // set the relay channel to the specified state 95 | onionPrint(ONION_SEVERITY_INFO, "> Setting RELAY%d to %s\n", channel, ( state == 1 ? "ON" : "OFF") ); 96 | status = mcp_setGpio(addr, channel, state); 97 | 98 | return status; 99 | } 100 | 101 | // set value to both GPIO0 and GPIO1 (both relay states) 102 | int relaySetAllChannels (int addr, int state) 103 | { 104 | int status, val; 105 | 106 | onionPrint(ONION_SEVERITY_INFO, "> Setting both RELAYS to %s\n", ( state == 1 ? "ON" : "OFF") ); 107 | 108 | // translate state to register value 109 | if (state == 0) { 110 | val = 0x0; 111 | } 112 | else { 113 | val = 0x3; // enable GPIO0 and GPIO1 114 | } 115 | 116 | // set the all relay channels to the specified state 117 | status = mcp_setAllGpio(addr, val); 118 | 119 | return status; 120 | } 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /test/servo_exp_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | LOW=11 4 | HIGH=90 5 | 6 | # initialize 7 | pwm-exp -i 0 25 8 | sleep 5 9 | 10 | # testing loop 11 | while [ 1 ] 12 | do 13 | # set duty cycle to low setting 14 | pwm-exp all $LOW 15 | sleep 2 16 | 17 | # set duty cycle to high setting 18 | pwm-exp all $HIGH 19 | sleep 2 20 | done 21 | 22 | -------------------------------------------------------------------------------- /tools/remote_compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ## define the remote server and package 4 | server="lazar@openwrt2.onion.io" 5 | remotePath="/home/lazar/OpenWRT-Buildroot/openwrt/dl" 6 | package="i2c-exp-driver" 7 | 8 | ## upload project to openwrt2.onion.io 9 | localPath="../$package" 10 | 11 | 12 | cmd="rsync -va --progress $localPath $server:$remotePath" 13 | echo "$cmd" 14 | eval "$cmd" 15 | 16 | 17 | ## create a tar from the file, run the compile 18 | cmd="ssh $server \"cd $remotePath && tar -zcvf $package.tar.gz $package && cd .. && make package/feeds/onion/$package/compile V=99\"" 19 | echo "$cmd" 20 | eval "$cmd" 21 | 22 | -------------------------------------------------------------------------------- /upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # check for argument 4 | if [ "$1" == "" ] 5 | then 6 | echo "ERROR: expecting Omega hex code as argument!" 7 | echo "$0 " 8 | exit 9 | fi 10 | 11 | localPath="rpcd/i2c-exp.sh" 12 | remotePath="/usr/libexec/rpcd/i2c_exp" 13 | 14 | cmd="rsync -va --progress $localPath root@omega-$1.local:$remotePath" 15 | echo "$cmd" 16 | eval "$cmd" 17 | 18 | --------------------------------------------------------------------------------